Coverage Report

Created: 2025-06-24 06:17

/rust/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.5/src/d2s.rs
Line
Count
Source (jump to first uncovered line)
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)]
39
0
pub fn decimal_length17(v: u64) -> u32 {
40
0
    // This is slightly faster than a loop.
41
0
    // The average output length is 16.38 digits, so we check high-to-low.
42
0
    // Function precondition: v is not an 18, 19, or 20-digit number.
43
0
    // (17 digits are sufficient for round-tripping.)
44
0
    debug_assert!(v < 100000000000000000);
45
46
0
    if v >= 10000000000000000 {
47
0
        17
48
0
    } else if v >= 1000000000000000 {
49
0
        16
50
0
    } else if v >= 100000000000000 {
51
0
        15
52
0
    } else if v >= 10000000000000 {
53
0
        14
54
0
    } else if v >= 1000000000000 {
55
0
        13
56
0
    } else if v >= 100000000000 {
57
0
        12
58
0
    } else if v >= 10000000000 {
59
0
        11
60
0
    } else if v >= 1000000000 {
61
0
        10
62
0
    } else if v >= 100000000 {
63
0
        9
64
0
    } else if v >= 10000000 {
65
0
        8
66
0
    } else if v >= 1000000 {
67
0
        7
68
0
    } else if v >= 100000 {
69
0
        6
70
0
    } else if v >= 10000 {
71
0
        5
72
0
    } else if v >= 1000 {
73
0
        4
74
0
    } else if v >= 100 {
75
0
        3
76
0
    } else if v >= 10 {
77
0
        2
78
    } else {
79
0
        1
80
    }
81
0
}
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)]
92
0
pub fn d2d(ieee_mantissa: u64, ieee_exponent: u32) -> FloatingDecimal64 {
93
0
    let (e2, m2) = if ieee_exponent == 0 {
94
0
        (
95
0
            // We subtract 2 so that the bounds computation has 2 additional bits.
96
0
            1 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32 - 2,
97
0
            ieee_mantissa,
98
0
        )
99
    } else {
100
0
        (
101
0
            ieee_exponent as i32 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32 - 2,
102
0
            (1u64 << DOUBLE_MANTISSA_BITS) | ieee_mantissa,
103
0
        )
104
    };
105
0
    let even = (m2 & 1) == 0;
106
0
    let accept_bounds = even;
107
0
108
0
    // Step 2: Determine the interval of valid decimal representations.
109
0
    let mv = 4 * m2;
110
    // Implicit bool -> int conversion. True is 1, false is 0.
111
0
    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
0
    let mut vp_uninit: MaybeUninit<u64> = MaybeUninit::uninit();
127
0
    #[cfg(maybe_uninit)]
128
0
    let mut vm_uninit: MaybeUninit<u64> = MaybeUninit::uninit();
129
0
    let e10: i32;
130
0
    let mut vm_is_trailing_zeros = false;
131
0
    let mut vr_is_trailing_zeros = false;
132
0
    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
0
        let q = log10_pow2(e2) - (e2 > 3) as u32;
136
0
        e10 = q as i32;
137
0
        let k = DOUBLE_POW5_INV_BITCOUNT + pow5bits(q as i32) - 1;
138
0
        let i = -e2 + q as i32 + k;
139
0
        vr = unsafe {
140
0
            mul_shift_all_64(
141
0
                m2,
142
0
                #[cfg(feature = "small")]
143
0
                &compute_inv_pow5(q),
144
0
                #[cfg(not(feature = "small"))]
145
0
                {
146
0
                    debug_assert!(q < DOUBLE_POW5_INV_SPLIT.len() as u32);
147
0
                    DOUBLE_POW5_INV_SPLIT.get_unchecked(q as usize)
148
0
                },
149
0
                i as u32,
150
0
                #[cfg(maybe_uninit)]
151
0
                {
152
0
                    vp_uninit.as_mut_ptr()
153
0
                },
154
0
                #[cfg(not(maybe_uninit))]
155
0
                {
156
0
                    &mut vp
157
0
                },
158
0
                #[cfg(maybe_uninit)]
159
0
                {
160
0
                    vm_uninit.as_mut_ptr()
161
0
                },
162
0
                #[cfg(not(maybe_uninit))]
163
0
                {
164
0
                    &mut vm
165
0
                },
166
0
                mm_shift,
167
0
            )
168
0
        };
169
0
        #[cfg(maybe_uninit)]
170
0
        {
171
0
            vp = unsafe { vp_uninit.assume_init() };
172
0
            vm = unsafe { vm_uninit.assume_init() };
173
0
        }
174
0
        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
0
            let mv_mod5 = (mv as u32).wrapping_sub(5u32.wrapping_mul(div5(mv) as u32));
179
0
            if mv_mod5 == 0 {
180
0
                vr_is_trailing_zeros = multiple_of_power_of_5(mv, q);
181
0
            } else if accept_bounds {
182
0
                // Same as min(e2 + (~mm & 1), pow5_factor(mm)) >= q
183
0
                // <=> e2 + (~mm & 1) >= q && pow5_factor(mm) >= q
184
0
                // <=> true && pow5_factor(mm) >= q, since e2 >= q.
185
0
                vm_is_trailing_zeros = multiple_of_power_of_5(mv - 1 - mm_shift as u64, q);
186
0
            } else {
187
0
                // Same as min(e2 + 1, pow5_factor(mp)) >= q.
188
0
                vp -= multiple_of_power_of_5(mv + 2, q) as u64;
189
0
            }
190
0
        }
191
    } else {
192
        // This expression is slightly faster than max(0, log10_pow5(-e2) - 1).
193
0
        let q = log10_pow5(-e2) - (-e2 > 1) as u32;
194
0
        e10 = q as i32 + e2;
195
0
        let i = -e2 - q as i32;
196
0
        let k = pow5bits(i) - DOUBLE_POW5_BITCOUNT;
197
0
        let j = q as i32 - k;
198
0
        vr = unsafe {
199
0
            mul_shift_all_64(
200
0
                m2,
201
0
                #[cfg(feature = "small")]
202
0
                &compute_pow5(i as u32),
203
0
                #[cfg(not(feature = "small"))]
204
0
                {
205
0
                    debug_assert!(i < DOUBLE_POW5_SPLIT.len() as i32);
206
0
                    DOUBLE_POW5_SPLIT.get_unchecked(i as usize)
207
0
                },
208
0
                j as u32,
209
0
                #[cfg(maybe_uninit)]
210
0
                {
211
0
                    vp_uninit.as_mut_ptr()
212
0
                },
213
0
                #[cfg(not(maybe_uninit))]
214
0
                {
215
0
                    &mut vp
216
0
                },
217
0
                #[cfg(maybe_uninit)]
218
0
                {
219
0
                    vm_uninit.as_mut_ptr()
220
0
                },
221
0
                #[cfg(not(maybe_uninit))]
222
0
                {
223
0
                    &mut vm
224
0
                },
225
0
                mm_shift,
226
0
            )
227
0
        };
228
0
        #[cfg(maybe_uninit)]
229
0
        {
230
0
            vp = unsafe { vp_uninit.assume_init() };
231
0
            vm = unsafe { vm_uninit.assume_init() };
232
0
        }
233
0
        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
0
            vr_is_trailing_zeros = true;
237
0
            if accept_bounds {
238
0
                // mm = mv - 1 - mm_shift, so it has 1 trailing 0 bit iff mm_shift == 1.
239
0
                vm_is_trailing_zeros = mm_shift == 1;
240
0
            } else {
241
0
                // mp = mv + 2, so it always has at least one trailing 0 bit.
242
0
                vp -= 1;
243
0
            }
244
0
        } else if q < 63 {
245
0
            // TODO(ulfjack): Use a tighter bound here.
246
0
            // We want to know if the full product has at least q trailing zeros.
247
0
            // We need to compute min(p2(mv), p5(mv) - e2) >= q
248
0
            // <=> p2(mv) >= q && p5(mv) - e2 >= q
249
0
            // <=> p2(mv) >= q (because -e2 >= q)
250
0
            vr_is_trailing_zeros = multiple_of_power_of_2(mv, q);
251
0
        }
252
    }
253
254
    // Step 4: Find the shortest decimal representation in the interval of valid representations.
255
0
    let mut removed = 0i32;
256
0
    let mut last_removed_digit = 0u8;
257
    // On average, we remove ~2 digits.
258
0
    let output = if vm_is_trailing_zeros || vr_is_trailing_zeros {
259
        // General case, which happens rarely (~0.7%).
260
        loop {
261
0
            let vp_div10 = div10(vp);
262
0
            let vm_div10 = div10(vm);
263
0
            if vp_div10 <= vm_div10 {
264
0
                break;
265
0
            }
266
0
            let vm_mod10 = (vm as u32).wrapping_sub(10u32.wrapping_mul(vm_div10 as u32));
267
0
            let vr_div10 = div10(vr);
268
0
            let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
269
0
            vm_is_trailing_zeros &= vm_mod10 == 0;
270
0
            vr_is_trailing_zeros &= last_removed_digit == 0;
271
0
            last_removed_digit = vr_mod10 as u8;
272
0
            vr = vr_div10;
273
0
            vp = vp_div10;
274
0
            vm = vm_div10;
275
0
            removed += 1;
276
        }
277
0
        if vm_is_trailing_zeros {
278
            loop {
279
0
                let vm_div10 = div10(vm);
280
0
                let vm_mod10 = (vm as u32).wrapping_sub(10u32.wrapping_mul(vm_div10 as u32));
281
0
                if vm_mod10 != 0 {
282
0
                    break;
283
0
                }
284
0
                let vp_div10 = div10(vp);
285
0
                let vr_div10 = div10(vr);
286
0
                let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
287
0
                vr_is_trailing_zeros &= last_removed_digit == 0;
288
0
                last_removed_digit = vr_mod10 as u8;
289
0
                vr = vr_div10;
290
0
                vp = vp_div10;
291
0
                vm = vm_div10;
292
0
                removed += 1;
293
            }
294
0
        }
295
0
        if vr_is_trailing_zeros && last_removed_digit == 5 && vr % 2 == 0 {
296
0
            // Round even if the exact number is .....50..0.
297
0
            last_removed_digit = 4;
298
0
        }
299
        // We need to take vr + 1 if vr is outside bounds or we need to round up.
300
0
        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
0
        let mut round_up = false;
305
0
        let vp_div100 = div100(vp);
306
0
        let vm_div100 = div100(vm);
307
0
        // Optimization: remove two digits at a time (~86.2%).
308
0
        if vp_div100 > vm_div100 {
309
0
            let vr_div100 = div100(vr);
310
0
            let vr_mod100 = (vr as u32).wrapping_sub(100u32.wrapping_mul(vr_div100 as u32));
311
0
            round_up = vr_mod100 >= 50;
312
0
            vr = vr_div100;
313
0
            vp = vp_div100;
314
0
            vm = vm_div100;
315
0
            removed += 2;
316
0
        }
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
0
            let vp_div10 = div10(vp);
323
0
            let vm_div10 = div10(vm);
324
0
            if vp_div10 <= vm_div10 {
325
0
                break;
326
0
            }
327
0
            let vr_div10 = div10(vr);
328
0
            let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
329
0
            round_up = vr_mod10 >= 5;
330
0
            vr = vr_div10;
331
0
            vp = vp_div10;
332
0
            vm = vm_div10;
333
0
            removed += 1;
334
        }
335
        // We need to take vr + 1 if vr is outside bounds or we need to round up.
336
0
        vr + (vr == vm || round_up) as u64
337
    };
338
0
    let exp = e10 + removed;
339
0
340
0
    FloatingDecimal64 {
341
0
        exponent: exp,
342
0
        mantissa: output,
343
0
    }
344
0
}