@@ -18,3 +18,145 @@ macro_rules! assert_approx_eq {
1818 "{} is not approximately equal to {}" , * a, * b) ;
1919 } )
2020}
21+
22+ macro_rules! from_str_radix_float_impl {
23+ ( $T: ty) => {
24+ fn from_str_radix( src: & str , radix: u32 )
25+ -> Result <$T, ParseFloatError > {
26+ use num:: FloatErrorKind :: * ;
27+ use num:: ParseFloatError as PFE ;
28+
29+ // Special values
30+ match src {
31+ "inf" => return Ok ( Float :: infinity( ) ) ,
32+ "-inf" => return Ok ( Float :: neg_infinity( ) ) ,
33+ "NaN" => return Ok ( Float :: nan( ) ) ,
34+ _ => { } ,
35+ }
36+
37+ let ( is_positive, src) = match src. slice_shift_char( ) {
38+ None => return Err ( PFE { kind: Empty } ) ,
39+ Some ( ( '-' , "" ) ) => return Err ( PFE { kind: Empty } ) ,
40+ Some ( ( '-' , src) ) => ( false , src) ,
41+ Some ( ( _, _) ) => ( true , src) ,
42+ } ;
43+
44+ // The significand to accumulate
45+ let mut sig = if is_positive { 0.0 } else { -0.0 } ;
46+ // Necessary to detect overflow
47+ let mut prev_sig = sig;
48+ let mut cs = src. chars( ) . enumerate( ) ;
49+ // Exponent prefix and exponent index offset
50+ let mut exp_info = None :: <( char , usize ) >;
51+
52+ // Parse the integer part of the significand
53+ for ( i, c) in cs. by_ref( ) {
54+ match c. to_digit( radix) {
55+ Some ( digit) => {
56+ // shift significand one digit left
57+ sig = sig * ( radix as $T) ;
58+
59+ // add/subtract current digit depending on sign
60+ if is_positive {
61+ sig = sig + ( ( digit as isize ) as $T) ;
62+ } else {
63+ sig = sig - ( ( digit as isize ) as $T) ;
64+ }
65+
66+ // Detect overflow by comparing to last value, except
67+ // if we've not seen any non-zero digits.
68+ if prev_sig != 0.0 {
69+ if is_positive && sig <= prev_sig
70+ { return Ok ( Float :: infinity( ) ) ; }
71+ if !is_positive && sig >= prev_sig
72+ { return Ok ( Float :: neg_infinity( ) ) ; }
73+
74+ // Detect overflow by reversing the shift-and-add process
75+ if is_positive && ( prev_sig != ( sig - digit as $T) / radix as $T)
76+ { return Ok ( Float :: infinity( ) ) ; }
77+ if !is_positive && ( prev_sig != ( sig + digit as $T) / radix as $T)
78+ { return Ok ( Float :: neg_infinity( ) ) ; }
79+ }
80+ prev_sig = sig;
81+ } ,
82+ None => match c {
83+ 'e' | 'E' | 'p' | 'P' => {
84+ exp_info = Some ( ( c, i + 1 ) ) ;
85+ break ; // start of exponent
86+ } ,
87+ '.' => {
88+ break ; // start of fractional part
89+ } ,
90+ _ => {
91+ return Err ( PFE { kind: Invalid } ) ;
92+ } ,
93+ } ,
94+ }
95+ }
96+
97+ // If we are not yet at the exponent parse the fractional
98+ // part of the significand
99+ if exp_info. is_none( ) {
100+ let mut power = 1.0 ;
101+ for ( i, c) in cs. by_ref( ) {
102+ match c. to_digit( radix) {
103+ Some ( digit) => {
104+ // Decrease power one order of magnitude
105+ power = power / ( radix as $T) ;
106+ // add/subtract current digit depending on sign
107+ sig = if is_positive {
108+ sig + ( digit as $T) * power
109+ } else {
110+ sig - ( digit as $T) * power
111+ } ;
112+ // Detect overflow by comparing to last value
113+ if is_positive && sig < prev_sig
114+ { return Ok ( Float :: infinity( ) ) ; }
115+ if !is_positive && sig > prev_sig
116+ { return Ok ( Float :: neg_infinity( ) ) ; }
117+ prev_sig = sig;
118+ } ,
119+ None => match c {
120+ 'e' | 'E' | 'p' | 'P' => {
121+ exp_info = Some ( ( c, i + 1 ) ) ;
122+ break ; // start of exponent
123+ } ,
124+ _ => {
125+ return Err ( PFE { kind: Invalid } ) ;
126+ } ,
127+ } ,
128+ }
129+ }
130+ }
131+
132+ // Parse and calculate the exponent
133+ let exp = match exp_info {
134+ Some ( ( c, offset) ) => {
135+ let base = match c {
136+ 'E' | 'e' if radix == 10 => 10.0 ,
137+ 'P' | 'p' if radix == 16 => 2.0 ,
138+ _ => return Err ( PFE { kind: Invalid } ) ,
139+ } ;
140+
141+ // Parse the exponent as decimal integer
142+ let src = & src[ offset..] ;
143+ let ( is_positive, exp) = match src. slice_shift_char( ) {
144+ Some ( ( '-' , src) ) => ( false , src. parse:: <usize >( ) ) ,
145+ Some ( ( '+' , src) ) => ( true , src. parse:: <usize >( ) ) ,
146+ Some ( ( _, _) ) => ( true , src. parse:: <usize >( ) ) ,
147+ None => return Err ( PFE { kind: Invalid } ) ,
148+ } ;
149+
150+ match ( is_positive, exp) {
151+ ( true , Ok ( exp) ) => base. powi( exp as i32 ) ,
152+ ( false , Ok ( exp) ) => 1.0 / base. powi( exp as i32 ) ,
153+ ( _, Err ( _) ) => return Err ( PFE { kind: Invalid } ) ,
154+ }
155+ } ,
156+ None => 1.0 , // no exponent
157+ } ;
158+
159+ Ok ( sig * exp)
160+ }
161+ }
162+ }
0 commit comments