@@ -18,3 +18,145 @@ macro_rules! assert_approx_eq {
18
18
"{} is not approximately equal to {}", *a, *b);
19
19
})
20
20
}
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