1use super::{BigDecimal, ParseBigDecimalError};
4use stdlib::num::FpCategory;
5
6use stdlib::cmp::{self, Ordering};
7
8use num_bigint::{BigInt, BigUint, Sign};
9use num_traits::Zero;
10
11
12pub(crate) fn try_parse_from_f32(n: f32) -> Result<BigDecimal, ParseBigDecimalError> {
17 use stdlib::num::FpCategory::*;
18 match n.classify() {
19 Nan => Err(ParseBigDecimalError::Other("NAN".into())),
20 Infinite => Err(ParseBigDecimalError::Other("Infinite".into())),
21 Subnormal => Ok(parse_from_f32_subnormal(n)),
22 Normal | Zero => Ok(parse_from_f32(n)),
23 }
24}
25
26
27fn split_f32_into_parts(f: f32) -> (u32, i64, Sign) {
34 let bits = f.to_bits();
35 let frac = (bits & ((1 << 23) - 1)) + (1 << 23);
36 let exp = (bits >> 23) & 0xFF;
37
38 let pow = exp as i64 - 127 - 23;
39
40 let sign_bit = bits & (1 << 31);
41 let sign = if sign_bit == 0 {
42 Sign::Plus
43 } else {
44 Sign::Minus
45 };
46
47 (frac, pow, sign)
48}
49
50
51pub(crate) fn parse_from_f32(n: f32) -> BigDecimal {
54 if n.classify() == FpCategory::Subnormal {
55 return parse_from_f32_subnormal(n);
56 }
57 let bits = n.to_bits();
58
59 if (bits << 1) == 0 {
60 return Zero::zero();
61 }
62
63 let (frac, pow, sign) = split_f32_into_parts(n);
65
66 let result;
67 let scale;
68 match pow.cmp(&0) {
69 Ordering::Equal => {
70 result = BigUint::from(frac);
71 scale = 0;
72 }
73 Ordering::Less => {
74 let trailing_zeros = cmp::min(frac.trailing_zeros(), -pow as u32);
75
76 let reduced_frac = frac >> trailing_zeros;
77 let reduced_pow = pow + trailing_zeros as i64;
78 debug_assert!(reduced_pow <= 0);
79
80 let shift = BigUint::from(5u8).pow(-reduced_pow as u32);
81
82 result = reduced_frac * shift;
83 scale = -reduced_pow;
84 }
85 Ordering::Greater => {
86 let shift = BigUint::from(2u8).pow(pow.abs() as u32);
87
88 result = frac * shift;
89 scale = 0;
90 }
91 }
92
93 BigDecimal {
94 int_val: BigInt::from_biguint(sign, result),
95 scale: scale,
96 }
97}
98
99pub(crate) fn parse_from_f32_subnormal(n: f32) -> BigDecimal {
101 debug_assert_eq!(n.classify(), FpCategory::Subnormal);
102 let bits = n.to_bits();
103
104 let sign_bit = bits >> 31;
105 debug_assert_eq!(bits >> 24, sign_bit << 7);
106
107 let frac = bits - (sign_bit << 31);
108
109 let five_to_149 = BigUint::from_slice(&[
111 1466336501, 2126633373, 2856417274, 1232167559, 2512314040, 1644054862,
112 3843013918, 3873995871, 858643596, 3706384338, 65604258
113 ]);
114
115 let sign = if sign_bit == 0 { Sign::Plus } else { Sign::Minus };
116 let magnitude = BigUint::from(frac) * five_to_149;
117 let scale = 149;
118 let result = BigDecimal::new(BigInt::from_biguint(sign, magnitude), scale);
119 return result;
120}
121
122
123#[cfg(test)]
124#[allow(non_snake_case)]
125mod test_parse_from_f32 {
126 use super::*;
127
128 include!("parsing.tests.parse_from_f32.rs");
129}
130
131
132pub(crate) fn try_parse_from_f64(n: f64) -> Result<BigDecimal, ParseBigDecimalError> {
137 use stdlib::num::FpCategory::*;
138 match n.classify() {
139 Nan => Err(ParseBigDecimalError::Other("NAN".into())),
140 Infinite => Err(ParseBigDecimalError::Other("Infinite".into())),
141 Subnormal => Ok(parse_from_f64_subnormal(n)),
142 Normal | Zero => Ok(parse_from_f64(n)),
143 }
144}
145
146
147fn split_f64_into_parts(f: f64) -> (u64, i64, Sign) {
154 let bits = f.to_bits();
155 let frac = (bits & ((1 << 52) - 1)) + (1 << 52);
156 let exp = (bits >> 52) & 0x7FF;
157
158 let pow = exp as i64 - 1023 - 52;
159
160 let sign_bit = bits & (1 << 63);
161 let sign = if sign_bit == 0 {
162 Sign::Plus
163 } else {
164 Sign::Minus
165 };
166
167 (frac, pow, sign)
168}
169
170pub(crate) fn parse_from_f64_subnormal(n: f64) -> BigDecimal {
172 debug_assert_eq!(n.classify(), FpCategory::Subnormal);
173 let bits = n.to_bits();
174
175 let sign_bit = bits >> 63;
176 debug_assert_eq!(bits >> 52, sign_bit << 11);
177
178 let five_to_1074 = BigUint::from_slice(&[
180 2993937753, 2678407619, 3969251600, 2340035423, 635686544, 3544357150, 2618749834,
181 3195461310, 2593678749, 4014721034, 2512738537, 1379014958, 2606506302, 1209795638,
182 3422246832, 2235398534, 2765471138, 3453720203, 3699786234, 1752628667, 3832472493,
183 2479745915, 4210941784, 2088904316, 4137646701, 3840319652, 3815898978, 2202136831,
184 1022273801, 1470939580, 2032173740, 4063736241, 2069243191, 4077145663, 4033014231,
185 1920904652, 4195885152, 3551517817, 4246423481, 2447790869, 1797774111, 11284306,
186 195273359, 3811183395, 4065514955, 3382133286, 1078447835, 2100087074, 3915378083,
187 1127077286, 1409634978, 2331452623, 1301118814, 3692061923, 2506161869, 4270519152,
188 1066095370, 212429084, 3729063602, 3175008277, 2075072468, 2136773221, 4247151843,
189 2395660055, 449096848, 2439918400, 1564416362, 3638689409, 3054795416, 1803373736,
190 1506581328, 2791252870, 3391180271, 1768177410, 3891987426, 3655546435, 3881223940,
191 903390128
192 ]);
193
194 let frac = bits - (sign_bit << 63);
195
196 let sign = if sign_bit == 0 { Sign::Plus } else { Sign::Minus };
197 let magnitude = BigUint::from(frac) * five_to_1074;
198 let scale = 1074;
199
200 return BigDecimal::new(BigInt::from_biguint(sign, magnitude), scale);
201}
202
203pub(crate) fn parse_from_f64(n: f64) -> BigDecimal {
208 if n.classify() == FpCategory::Subnormal {
209 return parse_from_f64_subnormal(n);
210 }
211
212 let bits = n.to_bits();
213
214 if (bits << 1) == 0 {
216 return Zero::zero();
217 }
218
219 let (frac, pow, sign) = split_f64_into_parts(n);
221 debug_assert!(frac > 0);
222
223 let result;
224 let scale;
225 match pow.cmp(&0) {
226 Ordering::Equal => {
227 result = BigUint::from(frac);
228 scale = 0;
229 }
230 Ordering::Less => {
231 let trailing_zeros = cmp::min(frac.trailing_zeros(), -pow as u32);
232
233 let reduced_frac = frac >> trailing_zeros;
234 let reduced_pow = pow + trailing_zeros as i64;
235 debug_assert!(reduced_pow <= 0);
236
237 let shift = BigUint::from(5u8).pow(-reduced_pow as u32);
238
239 result = reduced_frac * shift;
240 scale = -reduced_pow;
241 }
242 Ordering::Greater => {
243 let shift = BigUint::from(2u8).pow(pow as u32);
244 result = frac * shift;
245 scale = 0;
246 }
247 }
248
249 BigDecimal {
250 int_val: BigInt::from_biguint(sign, result),
251 scale: scale,
252 }
253}
254
255#[cfg(test)]
256#[allow(non_snake_case)]
257mod test_parse_from_f64 {
258 use super::*;
259
260 include!("parsing.tests.parse_from_f64.rs");
261}