ryu/
d2s.rs

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
21use core::ptr;
22
23#[cfg(maybe_uninit)]
24use core::mem::MaybeUninit;
25
26#[cfg(not(maybe_uninit))]
27use core::mem;
28
29use common::*;
30#[cfg(not(feature = "small"))]
31use d2s_full_table::*;
32use d2s_intrinsics::*;
33#[cfg(feature = "small")]
34use d2s_small_table::*;
35
36pub const DOUBLE_MANTISSA_BITS: u32 = 52;
37pub const DOUBLE_EXPONENT_BITS: u32 = 11;
38
39const DOUBLE_BIAS: i32 = 1023;
40const DOUBLE_POW5_INV_BITCOUNT: i32 = 122;
41const DOUBLE_POW5_BITCOUNT: i32 = 121;
42
43#[cfg(integer128)]
44#[cfg_attr(feature = "no-panic", inline)]
45fn mul_shift(m: u64, mul: &(u64, u64), j: u32) -> u64 {
46    let b0 = m as u128 * mul.0 as u128;
47    let b2 = m as u128 * mul.1 as u128;
48    (((b0 >> 64) + b2) >> (j - 64)) as u64
49}
50
51#[cfg(integer128)]
52#[cfg_attr(feature = "no-panic", inline)]
53unsafe fn mul_shift_all(
54    m: u64,
55    mul: &(u64, u64),
56    j: u32,
57    vp: *mut u64,
58    vm: *mut u64,
59    mm_shift: u32,
60) -> u64 {
61    ptr::write(vp, mul_shift(4 * m + 2, mul, j));
62    ptr::write(vm, mul_shift(4 * m - 1 - mm_shift as u64, mul, j));
63    mul_shift(4 * m, mul, j)
64}
65
66#[cfg(not(integer128))]
67#[cfg_attr(feature = "no-panic", inline)]
68unsafe fn mul_shift_all(
69    mut m: u64,
70    mul: &(u64, u64),
71    j: u32,
72    vp: *mut u64,
73    vm: *mut u64,
74    mm_shift: u32,
75) -> u64 {
76    m <<= 1;
77    // m is maximum 55 bits
78    let (lo, tmp) = umul128(m, mul.0);
79    let (mut mid, mut hi) = umul128(m, mul.1);
80    mid = mid.wrapping_add(tmp);
81    hi = hi.wrapping_add((mid < tmp) as u64); // overflow into hi
82
83    let lo2 = lo.wrapping_add(mul.0);
84    let mid2 = mid.wrapping_add(mul.1).wrapping_add((lo2 < lo) as u64);
85    let hi2 = hi.wrapping_add((mid2 < mid) as u64);
86    ptr::write(vp, shiftright128(mid2, hi2, j - 64 - 1));
87
88    if mm_shift == 1 {
89        let lo3 = lo.wrapping_sub(mul.0);
90        let mid3 = mid.wrapping_sub(mul.1).wrapping_sub((lo3 > lo) as u64);
91        let hi3 = hi.wrapping_sub((mid3 > mid) as u64);
92        ptr::write(vm, shiftright128(mid3, hi3, j - 64 - 1));
93    } else {
94        let lo3 = lo + lo;
95        let mid3 = mid.wrapping_add(mid).wrapping_add((lo3 < lo) as u64);
96        let hi3 = hi.wrapping_add(hi).wrapping_add((mid3 < mid) as u64);
97        let lo4 = lo3.wrapping_sub(mul.0);
98        let mid4 = mid3.wrapping_sub(mul.1).wrapping_sub((lo4 > lo3) as u64);
99        let hi4 = hi3.wrapping_sub((mid4 > mid3) as u64);
100        ptr::write(vm, shiftright128(mid4, hi4, j - 64));
101    }
102
103    shiftright128(mid, hi, j - 64 - 1)
104}
105
106#[cfg_attr(feature = "no-panic", inline)]
107pub fn decimal_length17(v: u64) -> u32 {
108    // This is slightly faster than a loop.
109    // The average output length is 16.38 digits, so we check high-to-low.
110    // Function precondition: v is not an 18, 19, or 20-digit number.
111    // (17 digits are sufficient for round-tripping.)
112    debug_assert!(v < 100000000000000000);
113
114    if v >= 10000000000000000 {
115        17
116    } else if v >= 1000000000000000 {
117        16
118    } else if v >= 100000000000000 {
119        15
120    } else if v >= 10000000000000 {
121        14
122    } else if v >= 1000000000000 {
123        13
124    } else if v >= 100000000000 {
125        12
126    } else if v >= 10000000000 {
127        11
128    } else if v >= 1000000000 {
129        10
130    } else if v >= 100000000 {
131        9
132    } else if v >= 10000000 {
133        8
134    } else if v >= 1000000 {
135        7
136    } else if v >= 100000 {
137        6
138    } else if v >= 10000 {
139        5
140    } else if v >= 1000 {
141        4
142    } else if v >= 100 {
143        3
144    } else if v >= 10 {
145        2
146    } else {
147        1
148    }
149}
150
151// A floating decimal representing m * 10^e.
152pub struct FloatingDecimal64 {
153    pub mantissa: u64,
154    // Decimal exponent's range is -324 to 308
155    // inclusive, and can fit in i16 if needed.
156    pub exponent: i32,
157}
158
159#[cfg_attr(feature = "no-panic", inline)]
160pub fn d2d(ieee_mantissa: u64, ieee_exponent: u32) -> FloatingDecimal64 {
161    let (e2, m2) = if ieee_exponent == 0 {
162        (
163            // We subtract 2 so that the bounds computation has 2 additional bits.
164            1 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32 - 2,
165            ieee_mantissa,
166        )
167    } else {
168        (
169            ieee_exponent as i32 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32 - 2,
170            (1u64 << DOUBLE_MANTISSA_BITS) | ieee_mantissa,
171        )
172    };
173    let even = (m2 & 1) == 0;
174    let accept_bounds = even;
175
176    // Step 2: Determine the interval of valid decimal representations.
177    let mv = 4 * m2;
178    // Implicit bool -> int conversion. True is 1, false is 0.
179    let mm_shift = (ieee_mantissa != 0 || ieee_exponent <= 1) as u32;
180    // We would compute mp and mm like this:
181    // uint64_t mp = 4 * m2 + 2;
182    // uint64_t mm = mv - 1 - mm_shift;
183
184    // Step 3: Convert to a decimal power base using 128-bit arithmetic.
185    let mut vr: u64;
186    let mut vp: u64;
187    let mut vm: u64;
188    #[cfg(not(maybe_uninit))]
189    {
190        vp = unsafe { mem::uninitialized() };
191        vm = unsafe { mem::uninitialized() };
192    }
193    #[cfg(maybe_uninit)]
194    let mut vp_uninit: MaybeUninit<u64> = MaybeUninit::uninit();
195    #[cfg(maybe_uninit)]
196    let mut vm_uninit: MaybeUninit<u64> = MaybeUninit::uninit();
197    let e10: i32;
198    let mut vm_is_trailing_zeros = false;
199    let mut vr_is_trailing_zeros = false;
200    if e2 >= 0 {
201        // I tried special-casing q == 0, but there was no effect on performance.
202        // This expression is slightly faster than max(0, log10_pow2(e2) - 1).
203        let q = log10_pow2(e2) - (e2 > 3) as u32;
204        e10 = q as i32;
205        let k = DOUBLE_POW5_INV_BITCOUNT + pow5bits(q as i32) - 1;
206        let i = -e2 + q as i32 + k;
207        vr = unsafe {
208            mul_shift_all(
209                m2,
210                #[cfg(feature = "small")]
211                &compute_inv_pow5(q),
212                #[cfg(not(feature = "small"))]
213                {
214                    debug_assert!(q < DOUBLE_POW5_INV_SPLIT.len() as u32);
215                    DOUBLE_POW5_INV_SPLIT.get_unchecked(q as usize)
216                },
217                i as u32,
218                #[cfg(maybe_uninit)]
219                {
220                    vp_uninit.as_mut_ptr()
221                },
222                #[cfg(not(maybe_uninit))]
223                {
224                    &mut vp
225                },
226                #[cfg(maybe_uninit)]
227                {
228                    vm_uninit.as_mut_ptr()
229                },
230                #[cfg(not(maybe_uninit))]
231                {
232                    &mut vm
233                },
234                mm_shift,
235            )
236        };
237        #[cfg(maybe_uninit)]
238        {
239            vp = unsafe { vp_uninit.assume_init() };
240            vm = unsafe { vm_uninit.assume_init() };
241        }
242        if q <= 21 {
243            // This should use q <= 22, but I think 21 is also safe. Smaller values
244            // may still be safe, but it's more difficult to reason about them.
245            // Only one of mp, mv, and mm can be a multiple of 5, if any.
246            let mv_mod5 = (mv as u32).wrapping_sub(5u32.wrapping_mul(div5(mv) as u32));
247            if mv_mod5 == 0 {
248                vr_is_trailing_zeros = multiple_of_power_of_5(mv, q);
249            } else if accept_bounds {
250                // Same as min(e2 + (~mm & 1), pow5_factor(mm)) >= q
251                // <=> e2 + (~mm & 1) >= q && pow5_factor(mm) >= q
252                // <=> true && pow5_factor(mm) >= q, since e2 >= q.
253                vm_is_trailing_zeros = multiple_of_power_of_5(mv - 1 - mm_shift as u64, q);
254            } else {
255                // Same as min(e2 + 1, pow5_factor(mp)) >= q.
256                vp -= multiple_of_power_of_5(mv + 2, q) as u64;
257            }
258        }
259    } else {
260        // This expression is slightly faster than max(0, log10_pow5(-e2) - 1).
261        let q = log10_pow5(-e2) - (-e2 > 1) as u32;
262        e10 = q as i32 + e2;
263        let i = -e2 - q as i32;
264        let k = pow5bits(i) - DOUBLE_POW5_BITCOUNT;
265        let j = q as i32 - k;
266        vr = unsafe {
267            mul_shift_all(
268                m2,
269                #[cfg(feature = "small")]
270                &compute_pow5(i as u32),
271                #[cfg(not(feature = "small"))]
272                {
273                    debug_assert!(i < DOUBLE_POW5_SPLIT.len() as i32);
274                    DOUBLE_POW5_SPLIT.get_unchecked(i as usize)
275                },
276                j as u32,
277                #[cfg(maybe_uninit)]
278                {
279                    vp_uninit.as_mut_ptr()
280                },
281                #[cfg(not(maybe_uninit))]
282                {
283                    &mut vp
284                },
285                #[cfg(maybe_uninit)]
286                {
287                    vm_uninit.as_mut_ptr()
288                },
289                #[cfg(not(maybe_uninit))]
290                {
291                    &mut vm
292                },
293                mm_shift,
294            )
295        };
296        #[cfg(maybe_uninit)]
297        {
298            vp = unsafe { vp_uninit.assume_init() };
299            vm = unsafe { vm_uninit.assume_init() };
300        }
301        if q <= 1 {
302            // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits.
303            // mv = 4 * m2, so it always has at least two trailing 0 bits.
304            vr_is_trailing_zeros = true;
305            if accept_bounds {
306                // mm = mv - 1 - mm_shift, so it has 1 trailing 0 bit iff mm_shift == 1.
307                vm_is_trailing_zeros = mm_shift == 1;
308            } else {
309                // mp = mv + 2, so it always has at least one trailing 0 bit.
310                vp -= 1;
311            }
312        } else if q < 63 {
313            // TODO(ulfjack): Use a tighter bound here.
314            // We want to know if the full product has at least q trailing zeros.
315            // We need to compute min(p2(mv), p5(mv) - e2) >= q
316            // <=> p2(mv) >= q && p5(mv) - e2 >= q
317            // <=> p2(mv) >= q (because -e2 >= q)
318            vr_is_trailing_zeros = multiple_of_power_of_2(mv, q);
319        }
320    }
321
322    // Step 4: Find the shortest decimal representation in the interval of valid representations.
323    let mut removed = 0i32;
324    let mut last_removed_digit = 0u8;
325    // On average, we remove ~2 digits.
326    let output = if vm_is_trailing_zeros || vr_is_trailing_zeros {
327        // General case, which happens rarely (~0.7%).
328        loop {
329            let vp_div10 = div10(vp);
330            let vm_div10 = div10(vm);
331            if vp_div10 <= vm_div10 {
332                break;
333            }
334            let vm_mod10 = (vm as u32).wrapping_sub(10u32.wrapping_mul(vm_div10 as u32));
335            let vr_div10 = div10(vr);
336            let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
337            vm_is_trailing_zeros &= vm_mod10 == 0;
338            vr_is_trailing_zeros &= last_removed_digit == 0;
339            last_removed_digit = vr_mod10 as u8;
340            vr = vr_div10;
341            vp = vp_div10;
342            vm = vm_div10;
343            removed += 1;
344        }
345        if vm_is_trailing_zeros {
346            loop {
347                let vm_div10 = div10(vm);
348                let vm_mod10 = (vm as u32).wrapping_sub(10u32.wrapping_mul(vm_div10 as u32));
349                if vm_mod10 != 0 {
350                    break;
351                }
352                let vp_div10 = div10(vp);
353                let vr_div10 = div10(vr);
354                let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
355                vr_is_trailing_zeros &= last_removed_digit == 0;
356                last_removed_digit = vr_mod10 as u8;
357                vr = vr_div10;
358                vp = vp_div10;
359                vm = vm_div10;
360                removed += 1;
361            }
362        }
363        if vr_is_trailing_zeros && last_removed_digit == 5 && vr % 2 == 0 {
364            // Round even if the exact number is .....50..0.
365            last_removed_digit = 4;
366        }
367        // We need to take vr + 1 if vr is outside bounds or we need to round up.
368        vr + ((vr == vm && (!accept_bounds || !vm_is_trailing_zeros)) || last_removed_digit >= 5)
369            as u64
370    } else {
371        // Specialized for the common case (~99.3%). Percentages below are relative to this.
372        let mut round_up = false;
373        let vp_div100 = div100(vp);
374        let vm_div100 = div100(vm);
375        // Optimization: remove two digits at a time (~86.2%).
376        if vp_div100 > vm_div100 {
377            let vr_div100 = div100(vr);
378            let vr_mod100 = (vr as u32).wrapping_sub(100u32.wrapping_mul(vr_div100 as u32));
379            round_up = vr_mod100 >= 50;
380            vr = vr_div100;
381            vp = vp_div100;
382            vm = vm_div100;
383            removed += 2;
384        }
385        // Loop iterations below (approximately), without optimization above:
386        // 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02%
387        // Loop iterations below (approximately), with optimization above:
388        // 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02%
389        loop {
390            let vp_div10 = div10(vp);
391            let vm_div10 = div10(vm);
392            if vp_div10 <= vm_div10 {
393                break;
394            }
395            let vr_div10 = div10(vr);
396            let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
397            round_up = vr_mod10 >= 5;
398            vr = vr_div10;
399            vp = vp_div10;
400            vm = vm_div10;
401            removed += 1;
402        }
403        // We need to take vr + 1 if vr is outside bounds or we need to round up.
404        vr + (vr == vm || round_up) as u64
405    };
406    let exp = e10 + removed;
407
408    FloatingDecimal64 {
409        exponent: exp,
410        mantissa: output,
411    }
412}