serde_json/lexical/algorithm.rs
1// Adapted from https://github.com/Alexhuszagh/rust-lexical.
2
3//! Algorithms to efficiently convert strings to floats.
4
5use super::bhcomp::*;
6use super::cached::*;
7use super::errors::*;
8use super::float::ExtendedFloat;
9use super::num::*;
10use super::small_powers::*;
11
12// FAST
13// ----
14
15/// Convert mantissa to exact value for a non-base2 power.
16///
17/// Returns the resulting float and if the value can be represented exactly.
18pub(crate) fn fast_path<F>(mantissa: u64, exponent: i32) -> Option<F>
19where
20 F: Float,
21{
22 // `mantissa >> (F::MANTISSA_SIZE+1) != 0` effectively checks if the
23 // value has a no bits above the hidden bit, which is what we want.
24 let (min_exp, max_exp) = F::exponent_limit();
25 let shift_exp = F::mantissa_limit();
26 let mantissa_size = F::MANTISSA_SIZE + 1;
27 if mantissa == 0 {
28 Some(F::ZERO)
29 } else if mantissa >> mantissa_size != 0 {
30 // Would require truncation of the mantissa.
31 None
32 } else if exponent == 0 {
33 // 0 exponent, same as value, exact representation.
34 let float = F::as_cast(mantissa);
35 Some(float)
36 } else if exponent >= min_exp && exponent <= max_exp {
37 // Value can be exactly represented, return the value.
38 // Do not use powi, since powi can incrementally introduce
39 // error.
40 let float = F::as_cast(mantissa);
41 Some(float.pow10(exponent))
42 } else if exponent >= 0 && exponent <= max_exp + shift_exp {
43 // Check to see if we have a disguised fast-path, where the
44 // number of digits in the mantissa is very small, but and
45 // so digits can be shifted from the exponent to the mantissa.
46 // https://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/
47 let small_powers = POW10_64;
48 let shift = exponent - max_exp;
49 let power = small_powers[shift as usize];
50
51 // Compute the product of the power, if it overflows,
52 // prematurely return early, otherwise, if we didn't overshoot,
53 // we can get an exact value.
54 let Some(value) = mantissa.checked_mul(power) else {
55 return None;
56 };
57 if value >> mantissa_size != 0 {
58 None
59 } else {
60 // Use powi, since it's correct, and faster on
61 // the fast-path.
62 let float = F::as_cast(value);
63 Some(float.pow10(max_exp))
64 }
65 } else {
66 // Cannot be exactly represented, exponent too small or too big,
67 // would require truncation.
68 None
69 }
70}
71
72// MODERATE
73// --------
74
75/// Multiply the floating-point by the exponent.
76///
77/// Multiply by pre-calculated powers of the base, modify the extended-
78/// float, and return if new value and if the value can be represented
79/// accurately.
80fn multiply_exponent_extended<F>(fp: &mut ExtendedFloat, exponent: i32, truncated: bool) -> bool
81where
82 F: Float,
83{
84 let powers = ExtendedFloat::get_powers();
85 let exponent = exponent.saturating_add(powers.bias);
86 let small_index = exponent % powers.step;
87 let large_index = exponent / powers.step;
88 if exponent < 0 {
89 // Guaranteed underflow (assign 0).
90 fp.mant = 0;
91 true
92 } else if large_index as usize >= powers.large.len() {
93 // Overflow (assign infinity)
94 fp.mant = 1 << 63;
95 fp.exp = 0x7FF;
96 true
97 } else {
98 // Within the valid exponent range, multiply by the large and small
99 // exponents and return the resulting value.
100
101 // Track errors to as a factor of unit in last-precision.
102 let mut errors: u32 = 0;
103 if truncated {
104 errors += u64::error_halfscale();
105 }
106
107 // Multiply by the small power.
108 // Check if we can directly multiply by an integer, if not,
109 // use extended-precision multiplication.
110 match fp
111 .mant
112 .overflowing_mul(powers.get_small_int(small_index as usize))
113 {
114 // Overflow, multiplication unsuccessful, go slow path.
115 (_, true) => {
116 fp.normalize();
117 fp.imul(&powers.get_small(small_index as usize));
118 errors += u64::error_halfscale();
119 }
120 // No overflow, multiplication successful.
121 (mant, false) => {
122 fp.mant = mant;
123 fp.normalize();
124 }
125 }
126
127 // Multiply by the large power
128 fp.imul(&powers.get_large(large_index as usize));
129 if errors > 0 {
130 errors += 1;
131 }
132 errors += u64::error_halfscale();
133
134 // Normalize the floating point (and the errors).
135 let shift = fp.normalize();
136 errors <<= shift;
137
138 u64::error_is_accurate::<F>(errors, fp)
139 }
140}
141
142/// Create a precise native float using an intermediate extended-precision float.
143///
144/// Return the float approximation and if the value can be accurately
145/// represented with mantissa bits of precision.
146#[inline]
147pub(crate) fn moderate_path<F>(
148 mantissa: u64,
149 exponent: i32,
150 truncated: bool,
151) -> (ExtendedFloat, bool)
152where
153 F: Float,
154{
155 let mut fp = ExtendedFloat {
156 mant: mantissa,
157 exp: 0,
158 };
159 let valid = multiply_exponent_extended::<F>(&mut fp, exponent, truncated);
160 (fp, valid)
161}
162
163// FALLBACK
164// --------
165
166/// Fallback path when the fast path does not work.
167///
168/// Uses the moderate path, if applicable, otherwise, uses the slow path
169/// as required.
170pub(crate) fn fallback_path<F>(
171 integer: &[u8],
172 fraction: &[u8],
173 mantissa: u64,
174 exponent: i32,
175 mantissa_exponent: i32,
176 truncated: bool,
177) -> F
178where
179 F: Float,
180{
181 // Moderate path (use an extended 80-bit representation).
182 let (fp, valid) = moderate_path::<F>(mantissa, mantissa_exponent, truncated);
183 if valid {
184 return fp.into_float::<F>();
185 }
186
187 // Slow path, fast path didn't work.
188 let b = fp.into_downward_float::<F>();
189 if b.is_special() {
190 // We have a non-finite number, we get to leave early.
191 b
192 } else {
193 bhcomp(b, integer, fraction, exponent)
194 }
195}