@@ -17,7 +17,7 @@ macro_rules! impl_for {
1717 ///
1818 /// Use `from_str_exact` to parse without rounding.
1919 fn from_str( str : & str ) -> Result <Self , Self :: Err > {
20- Self :: parse_str :: <false >( str )
20+ Self :: parse_str_with_scientific :: <false >( str )
2121 }
2222 }
2323
@@ -27,10 +27,12 @@ macro_rules! impl_for {
2727 ///
2828 /// Use the `FromStr` instance to parse with rounding.
2929 pub fn from_str_exact( str : & str ) -> Result <Self , ConvertError > {
30- Self :: parse_str :: <true >( str )
30+ Self :: parse_str_with_scientific :: <true >( str )
3131 }
3232
33- fn parse_str<const EXACT : bool >( str : & str ) -> Result <Self , ConvertError > {
33+ fn parse_str_without_scientific<const EXACT : bool >(
34+ str : & str ,
35+ ) -> Result <Self , ConvertError > {
3436 let str = str . trim( ) ;
3537
3638 let ( integral_str, mut fractional_str) = if let Some ( parts) = str . split_once( '.' ) {
@@ -97,6 +99,183 @@ macro_rules! impl_for {
9799 . map( Self :: from_bits)
98100 . ok_or_else( || ConvertError :: new( "too big number" ) )
99101 }
102+
103+ fn parse_str_with_scientific<const EXACT : bool >(
104+ str : & str ,
105+ ) -> Result <Self , ConvertError > {
106+ let str = str . trim( ) ;
107+
108+ let ( integral_and_fractional_str, exponent_str) = if let Some ( exponent_char) =
109+ str . chars( ) . find( |c| * c == 'e' || * c == 'E' )
110+ {
111+ if let Some ( parts) = str . split_once( exponent_char) {
112+ parts
113+ } else {
114+ // This error should never happen because `exponent_char` already found.
115+ return Err ( ConvertError :: new( "unable to split string by exponent char" ) ) ;
116+ }
117+ } else {
118+ return Self :: parse_str_without_scientific:: <EXACT >( str ) ;
119+ } ;
120+
121+ let mut exponent: i32 = exponent_str
122+ . parse( )
123+ . map_err( |_| ConvertError :: new( "can't parse exponent" ) ) ?;
124+
125+ let ( mut integral_str, mut fractional_str) =
126+ if let Some ( ( integral_str, fractional_str) ) =
127+ integral_and_fractional_str. split_once( '.' )
128+ {
129+ ( integral_str. to_owned( ) , fractional_str. to_owned( ) )
130+ } else {
131+ ( integral_and_fractional_str. to_owned( ) , "" . to_owned( ) )
132+ } ;
133+
134+ if !integral_str. is_empty( ) {
135+ let mut chars = integral_str. chars( ) ;
136+ let first_char = chars. next( ) . expect( "unreachable" ) ;
137+ if first_char == '+' || first_char == '-' {
138+ integral_str = chars. as_str( ) . to_owned( ) ;
139+ }
140+
141+ integral_str = integral_str. trim_start_matches( '0' ) . to_owned( ) ;
142+ }
143+
144+ let exponent_abs = exponent. abs( ) as usize ;
145+ // Main idea here is to keep one of parts empty or zero exponent.
146+ if exponent >= 0 {
147+ if exponent_abs >= fractional_str. len( ) {
148+ integral_str. push_str( fractional_str. as_str( ) ) ;
149+ exponent = exponent
150+ . checked_sub( fractional_str. len( ) as i32 ) // `as i32`` is safe because `exponent.abs() as usize >= fractional_str.len()`
151+ . ok_or( ConvertError :: new( "too small exponent" ) ) ?;
152+ fractional_str = "" . to_owned( ) ;
153+ } else {
154+ integral_str. push_str( & fractional_str[ 0 ..exponent_abs] ) ;
155+ fractional_str = fractional_str[ exponent_abs..] . to_owned( ) ;
156+ exponent = 0 ;
157+ }
158+ } else {
159+ if exponent_abs >= integral_str. len( ) {
160+ fractional_str. insert_str( 0 , integral_str. as_str( ) ) ;
161+ exponent = exponent
162+ . checked_add( integral_str. len( ) as i32 ) // `as i32`` is safe because `exponent.abs() as usize >= integral_str.len()`
163+ . ok_or( ConvertError :: new( "too large exponent" ) ) ?;
164+ integral_str = "" . to_owned( ) ;
165+ } else {
166+ fractional_str. insert_str(
167+ 0 ,
168+ & integral_str[ integral_str. len( ) - exponent_abs..integral_str. len( ) ] ,
169+ ) ;
170+ integral_str = integral_str[ ..integral_str. len( ) - exponent_abs] . to_owned( ) ;
171+ exponent = 0 ;
172+ }
173+ }
174+
175+ if !integral_str. is_empty( ) {
176+ integral_str = integral_str. trim_start_matches( '0' ) . to_owned( ) ;
177+ }
178+
179+ // `exponent_abs` must be reevaluated
180+ let exponent_abs = exponent. abs( ) as usize ;
181+
182+ debug_assert!(
183+ exponent == 0 || fractional_str. is_empty( ) || integral_str. is_empty( )
184+ ) ;
185+ let integral: $layout = if integral_str. is_empty( ) {
186+ debug_assert!( ( exponent == 0 || fractional_str. is_empty( ) ) && exponent <= 0 ) ;
187+ 0
188+ } else {
189+ // If at this point `integral_str` part can't be represent as `$layout` then
190+ // it obviously can't be represented as `$layout` after multiplication by `exponent`
191+ // because `exponent` here is not less than zero.
192+ debug_assert!( ( exponent == 0 || fractional_str. is_empty( ) ) && exponent >= 0 ) ;
193+ integral_str
194+ . parse( )
195+ . map_err( |_| ConvertError :: new( "can't parse integral part" ) ) ?
196+ } ;
197+
198+ if EXACT {
199+ // if `fractional_str` contains trailing zeroes this error will be misleading
200+ fractional_str = fractional_str. trim_end_matches( '0' ) . to_owned( ) ;
201+ if fractional_str. len( ) > ( Self :: PRECISION . abs( ) + exponent) as usize {
202+ return Err ( ConvertError :: new( "requested precision is too high" ) ) ;
203+ }
204+ }
205+
206+ let signum = if str . as_bytes( ) [ 0 ] == b'-' { -1 } else { 1 } ;
207+ let last_idx = Self :: PRECISION + exponent;
208+ let last_idx_abs = last_idx. abs( ) as usize ;
209+ let round = if !EXACT && last_idx >= 0 && last_idx_abs < fractional_str. len( ) {
210+ let extra = fractional_str. as_bytes( ) [ last_idx_abs] ;
211+ fractional_str = fractional_str[ ..last_idx_abs] . to_owned( ) ;
212+ Some ( signum) . filter( |_| extra >= b'5' )
213+ } else {
214+ None
215+ } ;
216+
217+ let ten: $layout = 10 ;
218+ let fractional_multiplier = ten. pow(
219+ ( fractional_str. len( ) + exponent_abs)
220+ . try_into( )
221+ . map_err( |_| ConvertError :: new( "too big fractional_str" ) ) ?,
222+ ) ;
223+
224+ if EXACT && fractional_multiplier > Self :: COEF {
225+ return Err ( ConvertError :: new( "requested precision is too high" ) ) ;
226+ }
227+
228+ debug_assert!( fractional_multiplier <= Self :: COEF ) ;
229+
230+ let fractional: Option <$layout> = if !fractional_str. is_empty( ) {
231+ Some (
232+ fractional_str
233+ . parse( )
234+ . map_err( |_| ConvertError :: new( "can't parse fractional part" ) ) ?,
235+ )
236+ } else {
237+ None
238+ } ;
239+
240+ let integral_multiplier = ten. pow(
241+ ( Self :: PRECISION + exponent)
242+ . try_into( )
243+ . map_err( |_| ConvertError :: new( "too big exponent" ) ) ?,
244+ ) ;
245+ let mut final_integral = integral
246+ . checked_mul( integral_multiplier)
247+ . ok_or( ConvertError :: new( "too big integral" ) ) ?;
248+
249+ if signum < 0 {
250+ final_integral = -final_integral;
251+ }
252+
253+ let mut final_fractional = fractional
254+ . map( |fractional| signum * Self :: COEF / fractional_multiplier * fractional) ;
255+
256+ if let Some ( round) = round {
257+ debug_assert!( !EXACT ) ;
258+ if let Some ( final_fractional_inner) = final_fractional. as_mut( ) {
259+ final_fractional = Some (
260+ final_fractional_inner
261+ . checked_add( round)
262+ . ok_or( ConvertError :: new( "requested precision is too high" ) ) ?,
263+ ) ;
264+ } else {
265+ final_integral = final_integral
266+ . checked_add( round)
267+ . ok_or( ConvertError :: new( "too big integral" ) ) ?;
268+ }
269+ }
270+
271+ if let Some ( & mut final_fractional) = final_fractional. as_mut( ) {
272+ final_integral = final_integral
273+ . checked_add( final_fractional)
274+ . ok_or( ConvertError :: new( "too big number" ) ) ?;
275+ }
276+
277+ Ok ( Self :: from_bits( final_integral) )
278+ }
100279 }
101280
102281 impl <P : Precision > Stringify for FixedPoint <$layout, P > {
0 commit comments