Coverage Report

Created: 2025-10-10 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ryu-1.0.20/src/d2s.rs
Line
Count
Source
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::{log10_pow2, log10_pow5, pow5bits};
22
#[cfg(not(feature = "small"))]
23
pub use crate::d2s_full_table::{DOUBLE_POW5_INV_SPLIT, DOUBLE_POW5_SPLIT};
24
use crate::d2s_intrinsics::{
25
    div10, div100, div5, mul_shift_all_64, multiple_of_power_of_2, multiple_of_power_of_5,
26
};
27
#[cfg(feature = "small")]
28
pub use crate::d2s_small_table::{compute_inv_pow5, compute_pow5};
29
use core::mem::MaybeUninit;
30
31
pub const DOUBLE_MANTISSA_BITS: u32 = 52;
32
pub const DOUBLE_EXPONENT_BITS: u32 = 11;
33
pub const DOUBLE_BIAS: i32 = 1023;
34
pub const DOUBLE_POW5_INV_BITCOUNT: i32 = 125;
35
pub const DOUBLE_POW5_BITCOUNT: i32 = 125;
36
37
#[cfg_attr(feature = "no-panic", inline)]
38
3.39k
pub fn decimal_length17(v: u64) -> u32 {
39
    // This is slightly faster than a loop.
40
    // The average output length is 16.38 digits, so we check high-to-low.
41
    // Function precondition: v is not an 18, 19, or 20-digit number.
42
    // (17 digits are sufficient for round-tripping.)
43
3.39k
    debug_assert!(v < 100000000000000000);
44
45
3.39k
    if v >= 10000000000000000 {
46
67
        17
47
3.32k
    } else if v >= 1000000000000000 {
48
145
        16
49
3.17k
    } else if v >= 100000000000000 {
50
66
        15
51
3.11k
    } else if v >= 10000000000000 {
52
61
        14
53
3.05k
    } else if v >= 1000000000000 {
54
57
        13
55
2.99k
    } else if v >= 100000000000 {
56
50
        12
57
2.94k
    } else if v >= 10000000000 {
58
58
        11
59
2.88k
    } else if v >= 1000000000 {
60
109
        10
61
2.77k
    } else if v >= 100000000 {
62
45
        9
63
2.73k
    } else if v >= 10000000 {
64
64
        8
65
2.66k
    } else if v >= 1000000 {
66
70
        7
67
2.59k
    } else if v >= 100000 {
68
61
        6
69
2.53k
    } else if v >= 10000 {
70
101
        5
71
2.43k
    } else if v >= 1000 {
72
113
        4
73
2.32k
    } else if v >= 100 {
74
243
        3
75
2.08k
    } else if v >= 10 {
76
577
        2
77
    } else {
78
1.50k
        1
79
    }
80
3.39k
}
81
82
// A floating decimal representing m * 10^e.
83
pub struct FloatingDecimal64 {
84
    pub mantissa: u64,
85
    // Decimal exponent's range is -324 to 308
86
    // inclusive, and can fit in i16 if needed.
87
    pub exponent: i32,
88
}
89
90
#[cfg_attr(feature = "no-panic", inline)]
91
3.39k
pub fn d2d(ieee_mantissa: u64, ieee_exponent: u32) -> FloatingDecimal64 {
92
3.39k
    let (e2, m2) = if ieee_exponent == 0 {
93
28
        (
94
28
            // We subtract 2 so that the bounds computation has 2 additional bits.
95
28
            1 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32 - 2,
96
28
            ieee_mantissa,
97
28
        )
98
    } else {
99
3.36k
        (
100
3.36k
            ieee_exponent as i32 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS as i32 - 2,
101
3.36k
            (1u64 << DOUBLE_MANTISSA_BITS) | ieee_mantissa,
102
3.36k
        )
103
    };
104
3.39k
    let even = (m2 & 1) == 0;
105
3.39k
    let accept_bounds = even;
106
107
    // Step 2: Determine the interval of valid decimal representations.
108
3.39k
    let mv = 4 * m2;
109
    // Implicit bool -> int conversion. True is 1, false is 0.
110
3.39k
    let mm_shift = (ieee_mantissa != 0 || ieee_exponent <= 1) as u32;
111
    // We would compute mp and mm like this:
112
    // uint64_t mp = 4 * m2 + 2;
113
    // uint64_t mm = mv - 1 - mm_shift;
114
115
    // Step 3: Convert to a decimal power base using 128-bit arithmetic.
116
    let mut vr: u64;
117
    let mut vp: u64;
118
    let mut vm: u64;
119
3.39k
    let mut vp_uninit: MaybeUninit<u64> = MaybeUninit::uninit();
120
3.39k
    let mut vm_uninit: MaybeUninit<u64> = MaybeUninit::uninit();
121
    let e10: i32;
122
3.39k
    let mut vm_is_trailing_zeros = false;
123
3.39k
    let mut vr_is_trailing_zeros = false;
124
3.39k
    if e2 >= 0 {
125
        // I tried special-casing q == 0, but there was no effect on performance.
126
        // This expression is slightly faster than max(0, log10_pow2(e2) - 1).
127
222
        let q = log10_pow2(e2) - (e2 > 3) as u32;
128
222
        e10 = q as i32;
129
222
        let k = DOUBLE_POW5_INV_BITCOUNT + pow5bits(q as i32) - 1;
130
222
        let i = -e2 + q as i32 + k;
131
        vr = unsafe {
132
222
            mul_shift_all_64(
133
222
                m2,
134
                #[cfg(feature = "small")]
135
                &compute_inv_pow5(q),
136
                #[cfg(not(feature = "small"))]
137
                {
138
222
                    debug_assert!(q < DOUBLE_POW5_INV_SPLIT.len() as u32);
139
222
                    DOUBLE_POW5_INV_SPLIT.get_unchecked(q as usize)
140
                },
141
222
                i as u32,
142
222
                vp_uninit.as_mut_ptr(),
143
222
                vm_uninit.as_mut_ptr(),
144
222
                mm_shift,
145
            )
146
        };
147
222
        vp = unsafe { vp_uninit.assume_init() };
148
222
        vm = unsafe { vm_uninit.assume_init() };
149
222
        if q <= 21 {
150
            // This should use q <= 22, but I think 21 is also safe. Smaller values
151
            // may still be safe, but it's more difficult to reason about them.
152
            // Only one of mp, mv, and mm can be a multiple of 5, if any.
153
186
            let mv_mod5 = (mv as u32).wrapping_sub(5u32.wrapping_mul(div5(mv) as u32));
154
186
            if mv_mod5 == 0 {
155
45
                vr_is_trailing_zeros = multiple_of_power_of_5(mv, q);
156
141
            } else if accept_bounds {
157
75
                // Same as min(e2 + (~mm & 1), pow5_factor(mm)) >= q
158
75
                // <=> e2 + (~mm & 1) >= q && pow5_factor(mm) >= q
159
75
                // <=> true && pow5_factor(mm) >= q, since e2 >= q.
160
75
                vm_is_trailing_zeros = multiple_of_power_of_5(mv - 1 - mm_shift as u64, q);
161
75
            } else {
162
66
                // Same as min(e2 + 1, pow5_factor(mp)) >= q.
163
66
                vp -= multiple_of_power_of_5(mv + 2, q) as u64;
164
66
            }
165
36
        }
166
    } else {
167
        // This expression is slightly faster than max(0, log10_pow5(-e2) - 1).
168
3.16k
        let q = log10_pow5(-e2) - (-e2 > 1) as u32;
169
3.16k
        e10 = q as i32 + e2;
170
3.16k
        let i = -e2 - q as i32;
171
3.16k
        let k = pow5bits(i) - DOUBLE_POW5_BITCOUNT;
172
3.16k
        let j = q as i32 - k;
173
        vr = unsafe {
174
3.16k
            mul_shift_all_64(
175
3.16k
                m2,
176
                #[cfg(feature = "small")]
177
                &compute_pow5(i as u32),
178
                #[cfg(not(feature = "small"))]
179
                {
180
3.16k
                    debug_assert!(i < DOUBLE_POW5_SPLIT.len() as i32);
181
3.16k
                    DOUBLE_POW5_SPLIT.get_unchecked(i as usize)
182
                },
183
3.16k
                j as u32,
184
3.16k
                vp_uninit.as_mut_ptr(),
185
3.16k
                vm_uninit.as_mut_ptr(),
186
3.16k
                mm_shift,
187
            )
188
        };
189
3.16k
        vp = unsafe { vp_uninit.assume_init() };
190
3.16k
        vm = unsafe { vm_uninit.assume_init() };
191
3.16k
        if q <= 1 {
192
            // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits.
193
            // mv = 4 * m2, so it always has at least two trailing 0 bits.
194
42
            vr_is_trailing_zeros = true;
195
42
            if accept_bounds {
196
29
                // mm = mv - 1 - mm_shift, so it has 1 trailing 0 bit iff mm_shift == 1.
197
29
                vm_is_trailing_zeros = mm_shift == 1;
198
29
            } else {
199
13
                // mp = mv + 2, so it always has at least one trailing 0 bit.
200
13
                vp -= 1;
201
13
            }
202
3.12k
        } else if q < 63 {
203
3.07k
            // TODO(ulfjack): Use a tighter bound here.
204
3.07k
            // We want to know if the full product has at least q trailing zeros.
205
3.07k
            // We need to compute min(p2(mv), p5(mv) - e2) >= q
206
3.07k
            // <=> p2(mv) >= q && p5(mv) - e2 >= q
207
3.07k
            // <=> p2(mv) >= q (because -e2 >= q)
208
3.07k
            vr_is_trailing_zeros = multiple_of_power_of_2(mv, q);
209
3.07k
        }
210
    }
211
212
    // Step 4: Find the shortest decimal representation in the interval of valid representations.
213
3.39k
    let mut removed = 0i32;
214
3.39k
    let mut last_removed_digit = 0u8;
215
    // On average, we remove ~2 digits.
216
3.39k
    let output = if vm_is_trailing_zeros || vr_is_trailing_zeros {
217
        // General case, which happens rarely (~0.7%).
218
        loop {
219
46.5k
            let vp_div10 = div10(vp);
220
46.5k
            let vm_div10 = div10(vm);
221
46.5k
            if vp_div10 <= vm_div10 {
222
2.96k
                break;
223
43.5k
            }
224
43.5k
            let vm_mod10 = (vm as u32).wrapping_sub(10u32.wrapping_mul(vm_div10 as u32));
225
43.5k
            let vr_div10 = div10(vr);
226
43.5k
            let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
227
43.5k
            vm_is_trailing_zeros &= vm_mod10 == 0;
228
43.5k
            vr_is_trailing_zeros &= last_removed_digit == 0;
229
43.5k
            last_removed_digit = vr_mod10 as u8;
230
43.5k
            vr = vr_div10;
231
43.5k
            vp = vp_div10;
232
43.5k
            vm = vm_div10;
233
43.5k
            removed += 1;
234
        }
235
2.96k
        if vm_is_trailing_zeros {
236
            loop {
237
76
                let vm_div10 = div10(vm);
238
76
                let vm_mod10 = (vm as u32).wrapping_sub(10u32.wrapping_mul(vm_div10 as u32));
239
76
                if vm_mod10 != 0 {
240
31
                    break;
241
45
                }
242
45
                let vp_div10 = div10(vp);
243
45
                let vr_div10 = div10(vr);
244
45
                let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
245
45
                vr_is_trailing_zeros &= last_removed_digit == 0;
246
45
                last_removed_digit = vr_mod10 as u8;
247
45
                vr = vr_div10;
248
45
                vp = vp_div10;
249
45
                vm = vm_div10;
250
45
                removed += 1;
251
            }
252
2.93k
        }
253
2.96k
        if vr_is_trailing_zeros && last_removed_digit == 5 && vr % 2 == 0 {
254
1
            // Round even if the exact number is .....50..0.
255
1
            last_removed_digit = 4;
256
2.96k
        }
257
        // We need to take vr + 1 if vr is outside bounds or we need to round up.
258
2.96k
        vr + ((vr == vm && (!accept_bounds || !vm_is_trailing_zeros)) || last_removed_digit >= 5)
259
            as u64
260
    } else {
261
        // Specialized for the common case (~99.3%). Percentages below are relative to this.
262
429
        let mut round_up = false;
263
429
        let vp_div100 = div100(vp);
264
429
        let vm_div100 = div100(vm);
265
        // Optimization: remove two digits at a time (~86.2%).
266
429
        if vp_div100 > vm_div100 {
267
394
            let vr_div100 = div100(vr);
268
394
            let vr_mod100 = (vr as u32).wrapping_sub(100u32.wrapping_mul(vr_div100 as u32));
269
394
            round_up = vr_mod100 >= 50;
270
394
            vr = vr_div100;
271
394
            vp = vp_div100;
272
394
            vm = vm_div100;
273
394
            removed += 2;
274
394
        }
275
        // Loop iterations below (approximately), without optimization above:
276
        // 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02%
277
        // Loop iterations below (approximately), with optimization above:
278
        // 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02%
279
        loop {
280
3.19k
            let vp_div10 = div10(vp);
281
3.19k
            let vm_div10 = div10(vm);
282
3.19k
            if vp_div10 <= vm_div10 {
283
429
                break;
284
2.76k
            }
285
2.76k
            let vr_div10 = div10(vr);
286
2.76k
            let vr_mod10 = (vr as u32).wrapping_sub(10u32.wrapping_mul(vr_div10 as u32));
287
2.76k
            round_up = vr_mod10 >= 5;
288
2.76k
            vr = vr_div10;
289
2.76k
            vp = vp_div10;
290
2.76k
            vm = vm_div10;
291
2.76k
            removed += 1;
292
        }
293
        // We need to take vr + 1 if vr is outside bounds or we need to round up.
294
429
        vr + (vr == vm || round_up) as u64
295
    };
296
3.39k
    let exp = e10 + removed;
297
298
3.39k
    FloatingDecimal64 {
299
3.39k
        exponent: exp,
300
3.39k
        mantissa: output,
301
3.39k
    }
302
3.39k
}