1 // Translated from C to Rust. The original C code can be found at
2 // https://github.com/ulfjack/ryu and carries the following license:
3 //
4 // Copyright 2018 Ulf Adams
5 //
6 // The contents of this file may be used under the terms of the Apache License,
7 // Version 2.0.
8 //
9 //    (See accompanying file LICENSE-Apache or copy at
10 //     http://www.apache.org/licenses/LICENSE-2.0)
11 //
12 // Alternatively, the contents of this file may be used under the terms of
13 // the Boost Software License, Version 1.0.
14 //    (See accompanying file LICENSE-Boost or copy at
15 //     https://www.boost.org/LICENSE_1_0.txt)
16 //
17 // Unless required by applicable law or agreed to in writing, this software
18 // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19 // KIND, either express or implied.
20 
21 use crate::common::*;
22 #[cfg(not(feature = "small"))]
23 pub use crate::d2s_full_table::*;
24 use crate::d2s_intrinsics::*;
25 #[cfg(feature = "small")]
26 pub use crate::d2s_small_table::*;
27 #[cfg(not(maybe_uninit))]
28 use core::mem;
29 #[cfg(maybe_uninit)]
30 use core::mem::MaybeUninit;
31 
32 pub const DOUBLE_MANTISSA_BITS: u32 = 52;
33 pub const DOUBLE_EXPONENT_BITS: u32 = 11;
34 pub const DOUBLE_BIAS: i32 = 1023;
35 pub const DOUBLE_POW5_INV_BITCOUNT: i32 = 125;
36 pub const DOUBLE_POW5_BITCOUNT: i32 = 125;
37 
38 #[cfg_attr(feature = "no-panic", inline)]
decimal_length17(v: u64) -> u3239 pub fn decimal_length17(v: u64) -> u32 {
40     // This is slightly faster than a loop.
41     // The average output length is 16.38 digits, so we check high-to-low.
42     // Function precondition: v is not an 18, 19, or 20-digit number.
43     // (17 digits are sufficient for round-tripping.)
44     debug_assert!(v < 100000000000000000);
45 
46     if v >= 10000000000000000 {
47         17
48     } else if v >= 1000000000000000 {
49         16
50     } else if v >= 100000000000000 {
51         15
52     } else if v >= 10000000000000 {
53         14
54     } else if v >= 1000000000000 {
55         13
56     } else if v >= 100000000000 {
57         12
58     } else if v >= 10000000000 {
59         11
60     } else if v >= 1000000000 {
61         10
62     } else if v >= 100000000 {
63         9
64     } else if v >= 10000000 {
65         8
66     } else if v >= 1000000 {
67         7
68     } else if v >= 100000 {
69         6
70     } else if v >= 10000 {
71         5
72     } else if v >= 1000 {
73         4
74     } else if v >= 100 {
75         3
76     } else if v >= 10 {
77         2
78     } else {
79         1
80     }
81 }
82 
83 // A floating decimal representing m * 10^e.
84 pub struct FloatingDecimal64 {
85     pub mantissa: u64,
86     // Decimal exponent's range is -324 to 308
87     // inclusive, and can fit in i16 if needed.
88     pub exponent: i32,
89 }
90 
91 #[cfg_attr(feature = "no-panic", inline)]
d2d(ieee_mantissa: u64, ieee_exponent: u32) -> FloatingDecimal6492 pub fn d2d(ieee_mantissa: u64, ieee_exponent: u32) -> FloatingDecimal64 {
93     let (e2, m2) = if ieee_exponent == 0 {
94         (
95             // We subtract 2 so that the bounds computation has 2 additional bits.
96             1 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32 - 2,
97             ieee_mantissa,
98         )
99     } else {
100         (
101             ieee_exponent as i32 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32 - 2,
102             (1u64 << DOUBLE_MANTISSA_BITS) | ieee_mantissa,
103         )
104     };
105     let even = (m2 & 1) == 0;
106     let accept_bounds = even;
107 
108     // Step 2: Determine the interval of valid decimal representations.
109     let mv = 4 * m2;
110     // Implicit bool -> int conversion. True is 1, false is 0.
111     let mm_shift = (ieee_mantissa != 0 || ieee_exponent <= 1) as u32;
112     // We would compute mp and mm like this:
113     // uint64_t mp = 4 * m2 + 2;
114     // uint64_t mm = mv - 1 - mm_shift;
115 
116     // Step 3: Convert to a decimal power base using 128-bit arithmetic.
117     let mut vr: u64;
118     let mut vp: u64;
119     let mut vm: u64;
120     #[cfg(not(maybe_uninit))]
121     {
122         vp = unsafe { mem::uninitialized() };
123         vm = unsafe { mem::uninitialized() };
124     }
125     #[cfg(maybe_uninit)]
126     let mut vp_uninit: MaybeUninit<u64> = MaybeUninit::uninit();
127     #[cfg(maybe_uninit)]
128     let mut vm_uninit: MaybeUninit<u64> = MaybeUninit::uninit();
129     let e10: i32;
130     let mut vm_is_trailing_zeros = false;
131     let mut vr_is_trailing_zeros = false;
132     if e2 >= 0 {
133         // I tried special-casing q == 0, but there was no effect on performance.
134         // This expression is slightly faster than max(0, log10_pow2(e2) - 1).
135         let q = log10_pow2(e2) - (e2 > 3) as u32;
136         e10 = q as i32;
137         let k = DOUBLE_POW5_INV_BITCOUNT + pow5bits(q as i32) - 1;
138         let i = -e2 + q as i32 + k;
139         vr = unsafe {
140             mul_shift_all_64(
141                 m2,
142                 #[cfg(feature = "small")]
143                 &compute_inv_pow5(q),
144                 #[cfg(not(feature = "small"))]
145                 {
146                     debug_assert!(q < DOUBLE_POW5_INV_SPLIT.len() as u32);
147                     DOUBLE_POW5_INV_SPLIT.get_unchecked(q as usize)
148                 },
149                 i as u32,
150                 #[cfg(maybe_uninit)]
151                 {
152                     vp_uninit.as_mut_ptr()
153                 },
154                 #[cfg(not(maybe_uninit))]
155                 {
156                     &mut vp
157                 },
158                 #[cfg(maybe_uninit)]
159                 {
160                     vm_uninit.as_mut_ptr()
161                 },
162                 #[cfg(not(maybe_uninit))]
163                 {
164                     &mut vm
165                 },
166                 mm_shift,
167             )
168         };
169         #[cfg(maybe_uninit)]
170         {
171             vp = unsafe { vp_uninit.assume_init() };
172             vm = unsafe { vm_uninit.assume_init() };
173         }
174         if q <= 21 {
175             // This should use q <= 22, but I think 21 is also safe. Smaller values
176             // may still be safe, but it's more difficult to reason about them.
177             // Only one of mp, mv, and mm can be a multiple of 5, if any.
178             let mv_mod5 = (mv as u32).wrapping_sub(5u32.wrapping_mul(div5(mv) as u32));
179             if mv_mod5 == 0 {
180                 vr_is_trailing_zeros = multiple_of_power_of_5(mv, q);
181             } else if accept_bounds {
182                 // Same as min(e2 + (~mm & 1), pow5_factor(mm)) >= q
183                 // <=> e2 + (~mm & 1) >= q && pow5_factor(mm) >= q
184                 // <=> true && pow5_factor(mm) >= q, since e2 >= q.
185                 vm_is_trailing_zeros = multiple_of_power_of_5(mv - 1 - mm_shift as u64, q);
186             } else {
187                 // Same as min(e2 + 1, pow5_factor(mp)) >= q.
188                 vp -= multiple_of_power_of_5(mv + 2, q) as u64;
189             }
190         }
191     } else {
192         // This expression is slightly faster than max(0, log10_pow5(-e2) - 1).
193         let q = log10_pow5(-e2) - (-e2 > 1) as u32;
194         e10 = q as i32 + e2;
195         let i = -e2 - q as i32;
196         let k = pow5bits(i) - DOUBLE_POW5_BITCOUNT;
197         let j = q as i32 - k;
198         vr = unsafe {
199             mul_shift_all_64(
200                 m2,
201                 #[cfg(feature = "small")]
202                 &compute_pow5(i as u32),
203                 #[cfg(not(feature = "small"))]
204                 {
205                     debug_assert!(i < DOUBLE_POW5_SPLIT.len() as i32);
206                     DOUBLE_POW5_SPLIT.get_unchecked(i as usize)
207                 },
208                 j as u32,
209                 #[cfg(maybe_uninit)]
210                 {
211                     vp_uninit.as_mut_ptr()
212                 },
213                 #[cfg(not(maybe_uninit))]
214                 {
215                     &mut vp
216                 },
217                 #[cfg(maybe_uninit)]
218                 {
219                     vm_uninit.as_mut_ptr()
220                 },
221                 #[cfg(not(maybe_uninit))]
222                 {
223                     &mut vm
224                 },
225                 mm_shift,
226             )
227         };
228         #[cfg(maybe_uninit)]
229         {
230             vp = unsafe { vp_uninit.assume_init() };
231             vm = unsafe { vm_uninit.assume_init() };
232         }
233         if q <= 1 {
234             // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits.
235             // mv = 4 * m2, so it always has at least two trailing 0 bits.
236             vr_is_trailing_zeros = true;
237             if accept_bounds {
238                 // mm = mv - 1 - mm_shift, so it has 1 trailing 0 bit iff mm_shift == 1.
239                 vm_is_trailing_zeros = mm_shift == 1;
240             } else {
241                 // mp = mv + 2, so it always has at least one trailing 0 bit.
242                 vp -= 1;
243             }
244         } else if q < 63 {
245             // TODO(ulfjack): Use a tighter bound here.
246             // We want to know if the full product has at least q trailing zeros.
247             // We need to compute min(p2(mv), p5(mv) - e2) >= q
248             // <=> p2(mv) >= q && p5(mv) - e2 >= q
249             // <=> p2(mv) >= q (because -e2 >= q)
250             vr_is_trailing_zeros = multiple_of_power_of_2(mv, q);
251         }
252     }
253 
254     // Step 4: Find the shortest decimal representation in the interval of valid representations.
255     let mut removed = 0i32;
256     let mut last_removed_digit = 0u8;
257     // On average, we remove ~2 digits.
258     let output = if vm_is_trailing_zeros || vr_is_trailing_zeros {
259         // General case, which happens rarely (~0.7%).
260         loop {
261             let vp_div10 = div10(vp);
262             let vm_div10 = div10(vm);
263             if vp_div10 <= vm_div10 {
264                 break;
265             }
266             let vm_mod10 = (vm as u32).wrapping_sub(10u32.wrapping_mul(vm_div10 as u32));
267             let vr_div10 = div10(vr);
268             let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
269             vm_is_trailing_zeros &= vm_mod10 == 0;
270             vr_is_trailing_zeros &= last_removed_digit == 0;
271             last_removed_digit = vr_mod10 as u8;
272             vr = vr_div10;
273             vp = vp_div10;
274             vm = vm_div10;
275             removed += 1;
276         }
277         if vm_is_trailing_zeros {
278             loop {
279                 let vm_div10 = div10(vm);
280                 let vm_mod10 = (vm as u32).wrapping_sub(10u32.wrapping_mul(vm_div10 as u32));
281                 if vm_mod10 != 0 {
282                     break;
283                 }
284                 let vp_div10 = div10(vp);
285                 let vr_div10 = div10(vr);
286                 let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
287                 vr_is_trailing_zeros &= last_removed_digit == 0;
288                 last_removed_digit = vr_mod10 as u8;
289                 vr = vr_div10;
290                 vp = vp_div10;
291                 vm = vm_div10;
292                 removed += 1;
293             }
294         }
295         if vr_is_trailing_zeros && last_removed_digit == 5 && vr % 2 == 0 {
296             // Round even if the exact number is .....50..0.
297             last_removed_digit = 4;
298         }
299         // We need to take vr + 1 if vr is outside bounds or we need to round up.
300         vr + ((vr == vm && (!accept_bounds || !vm_is_trailing_zeros)) || last_removed_digit >= 5)
301             as u64
302     } else {
303         // Specialized for the common case (~99.3%). Percentages below are relative to this.
304         let mut round_up = false;
305         let vp_div100 = div100(vp);
306         let vm_div100 = div100(vm);
307         // Optimization: remove two digits at a time (~86.2%).
308         if vp_div100 > vm_div100 {
309             let vr_div100 = div100(vr);
310             let vr_mod100 = (vr as u32).wrapping_sub(100u32.wrapping_mul(vr_div100 as u32));
311             round_up = vr_mod100 >= 50;
312             vr = vr_div100;
313             vp = vp_div100;
314             vm = vm_div100;
315             removed += 2;
316         }
317         // Loop iterations below (approximately), without optimization above:
318         // 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02%
319         // Loop iterations below (approximately), with optimization above:
320         // 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02%
321         loop {
322             let vp_div10 = div10(vp);
323             let vm_div10 = div10(vm);
324             if vp_div10 <= vm_div10 {
325                 break;
326             }
327             let vr_div10 = div10(vr);
328             let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
329             round_up = vr_mod10 >= 5;
330             vr = vr_div10;
331             vp = vp_div10;
332             vm = vm_div10;
333             removed += 1;
334         }
335         // We need to take vr + 1 if vr is outside bounds or we need to round up.
336         vr + (vr == vm || round_up) as u64
337     };
338     let exp = e10 + removed;
339 
340     FloatingDecimal64 {
341         exponent: exp,
342         mantissa: output,
343     }
344 }
345