Coverage Report

Created: 2026-07-16 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.23/src/lib.rs
Line
Count
Source
1
//! [![github]](https://github.com/dtolnay/zmij) [![crates-io]](https://crates.io/crates/zmij) [![docs-rs]](https://docs.rs/zmij)
2
//!
3
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6
//!
7
//! <br>
8
//!
9
//! A double-to-string conversion algorithm based on [Schubfach] and [yy].
10
//!
11
//! This Rust implementation is a line-by-line port of Victor Zverovich's
12
//! implementation in C++, <https://github.com/vitaut/zmij>.
13
//!
14
//! [Schubfach]: https://fmt.dev/papers/Schubfach4.pdf
15
//! [yy]: https://github.com/ibireme/c_numconv_benchmark/blob/master/vendor/yy_double/yy_double.c
16
//!
17
//! <br>
18
//!
19
//! # Example
20
//!
21
//! ```
22
//! fn main() {
23
//!     let mut buffer = zmij::Buffer::new();
24
//!     let printed = buffer.format(1.234);
25
//!     assert_eq!(printed, "1.234");
26
//! }
27
//! ```
28
//!
29
//! <br>
30
//!
31
//! ## Performance
32
//!
33
//! The [dtoa-benchmark] compares this library and other Rust floating point
34
//! formatting implementations across a range of precisions. The vertical axis
35
//! in this chart shows nanoseconds taken by a single execution of
36
//! `zmij::Buffer::new().format_finite(value)` so a lower result indicates a
37
//! faster library.
38
//!
39
//! [dtoa-benchmark]: https://github.com/dtolnay/dtoa-benchmark
40
//!
41
//! ![performance](https://raw.githubusercontent.com/dtolnay/zmij/master/dtoa-benchmark.png)
42
43
#![no_std]
44
#![doc(html_root_url = "https://docs.rs/zmij/1.0.23")]
45
#![deny(unsafe_op_in_unsafe_fn)]
46
#![allow(non_camel_case_types, non_snake_case)]
47
#![allow(
48
    clippy::blocks_in_conditions,
49
    clippy::cast_possible_truncation,
50
    clippy::cast_possible_wrap,
51
    clippy::cast_ptr_alignment,
52
    clippy::cast_sign_loss,
53
    clippy::doc_markdown,
54
    clippy::incompatible_msrv,
55
    clippy::items_after_statements,
56
    clippy::manual_ilog2,
57
    clippy::many_single_char_names,
58
    clippy::modulo_one,
59
    clippy::must_use_candidate,
60
    clippy::needless_doctest_main,
61
    clippy::needless_late_init,
62
    clippy::never_loop,
63
    clippy::redundant_else,
64
    clippy::similar_names,
65
    clippy::too_many_arguments,
66
    clippy::too_many_lines,
67
    clippy::unreadable_literal,
68
    clippy::used_underscore_items,
69
    clippy::while_immutable_condition,
70
    clippy::wildcard_imports
71
)]
72
73
#[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
74
mod stdarch_x86;
75
#[cfg(test)]
76
mod tests;
77
mod traits;
78
79
#[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
80
use crate::stdarch_x86::{
81
    __m128i, _mm_add_epi64, _mm_cmpgt_epi8, _mm_cvtsi128_si64, _mm_load_si128, _mm_movemask_epi8,
82
    _mm_mul_epu32, _mm_mulhi_epu16, _mm_mullo_epi16, _mm_or_si128, _mm_set_epi64x,
83
    _mm_setzero_si128, _mm_srli_epi64,
84
};
85
#[cfg(all(
86
    target_arch = "x86_64",
87
    target_feature = "sse2",
88
    target_feature = "sse4.1",
89
    not(miri)
90
))]
91
use crate::stdarch_x86::{
92
    _mm_insert_epi64, _mm_mullo_epi32, _mm_shuffle_epi8, _mm_srli_epi32, _mm_storeu_si128,
93
};
94
#[cfg(all(
95
    target_arch = "x86_64",
96
    target_feature = "sse2",
97
    not(target_feature = "sse4.1"),
98
    not(miri)
99
))]
100
use crate::stdarch_x86::{
101
    _mm_shuffle_epi32, _mm_slli_epi16, _mm_slli_epi32, _mm_srli_epi16, _mm_sub_epi16, _MM_SHUFFLE,
102
};
103
use crate::traits::Float as _;
104
#[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
105
use core::arch::aarch64::{
106
    int16x8_t, int32x2_t, int32x4_t, uint16x8_t, uint64x1_t, uint8x16_t, vaddq_u16, vcgtzq_s8,
107
    vcombine_s32, vcreate_u64, vdup_n_s32, vdupq_n_s8, vdupq_n_u8, vget_lane_u64, vget_low_u8,
108
    vld1q_u8, vmla_n_s32, vmlaq_n_s16, vmlaq_n_s32, vorrq_u8, vqdmulh_n_s32, vqdmulhq_n_s16,
109
    vqdmulhq_n_s32, vqtbl1q_u8, vreinterpret_s32_u32, vreinterpret_s32_u64, vreinterpret_u16_s32,
110
    vreinterpret_u32_s32, vreinterpret_u64_u8, vreinterpretq_s16_s32, vreinterpretq_s32_u32,
111
    vreinterpretq_s8_u8, vreinterpretq_u16_s8, vreinterpretq_u16_u8, vreinterpretq_u64_u8,
112
    vreinterpretq_u8_s16, vreinterpretq_u8_u64, vrev64q_u8, vsetq_lane_u64, vshll_n_u16,
113
    vshr_n_u32, vshrn_n_u16, vst1q_u8,
114
};
115
#[cfg(all(any(target_arch = "aarch64", target_arch = "x86_64"), not(miri)))]
116
use core::arch::asm;
117
use core::mem::{self, MaybeUninit};
118
use core::ops::RangeInclusive;
119
use core::ptr;
120
use core::slice;
121
use core::str;
122
#[cfg(feature = "no-panic")]
123
use no_panic::no_panic;
124
125
const BUFFER_SIZE: usize = 24;
126
const NAN: &str = "NaN";
127
const INFINITY: &str = "inf";
128
const NEG_INFINITY: &str = "-inf";
129
130
// Declares struct members that must live in memory on ARM64 but are encoded as
131
// immediates in the x64 assembly.
132
struct AArch64Mem<const VALUE: u64> {
133
    #[cfg(target_arch = "aarch64")]
134
    value: u64,
135
}
136
137
impl<const VALUE: u64> AArch64Mem<VALUE> {
138
0
    const fn new() -> Self {
139
0
        AArch64Mem {
140
0
            #[cfg(target_arch = "aarch64")]
141
0
            value: VALUE,
142
0
        }
143
0
    }
144
145
    #[cfg_attr(not(target_arch = "aarch64"), allow(clippy::unused_self))]
146
0
    const fn get(&self) -> u64 {
147
        #[cfg(target_arch = "aarch64")]
148
        {
149
            self.value
150
        }
151
152
        #[cfg(not(target_arch = "aarch64"))]
153
        {
154
0
            VALUE
155
        }
156
0
    }
Unexecuted instantiation: <zmij::AArch64Mem<1000000000000000>>::get
Unexecuted instantiation: <zmij::AArch64Mem<9223372036854775814>>::get
157
}
158
159
#[derive(Copy, Clone)]
160
#[cfg_attr(test, derive(Debug, PartialEq))]
161
struct uint128 {
162
    hi: u64,
163
    lo: u64,
164
}
165
166
// Use umul128_hi64 for division.
167
const USE_UMUL128_HI64: bool = cfg!(target_vendor = "apple");
168
169
// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
170
0
const fn umul128(x: u64, y: u64) -> u128 {
171
0
    x as u128 * y as u128
172
0
}
173
174
#[inline]
175
0
const fn umul128_hi64(x: u64, y: u64) -> u64 {
176
0
    (umul128(x, y) >> 64) as u64
177
0
}
178
179
// Returns (x * y + c) >> 64.
180
#[cfg_attr(feature = "no-panic", no_panic)]
181
0
fn umul128_add_hi64(x: u64, y: u64, c: u64) -> u64 {
182
0
    ((u128::from(x) * u128::from(y) + u128::from(c)) >> 64) as u64
183
0
}
184
185
#[cfg_attr(feature = "no-panic", no_panic)]
186
0
fn umul192_hi128(x_hi: u64, x_lo: u64, y: u64) -> uint128 {
187
0
    let p = umul128(x_hi, y);
188
0
    let lo = (p as u64).wrapping_add((umul128(x_lo, y) >> 64) as u64);
189
0
    uint128 {
190
0
        hi: (p >> 64) as u64 + u64::from(lo < p as u64),
191
0
        lo,
192
0
    }
193
0
}
194
195
// Returns x / 10 for x <= 2**62.
196
#[cfg_attr(feature = "no-panic", no_panic)]
197
0
fn div10(x: u64) -> u64 {
198
0
    debug_assert!(x < (1 << 62));
199
    // ceil(2**64 / 10) computed as (1 << 63) / 5 + 1 to avoid int128.
200
    const DIV10_SIG64: u64 = (1 << 63) / 5 + 1;
201
0
    umul128_hi64(x, DIV10_SIG64)
202
0
}
203
204
// Computes the decimal exponent as floor(log10(2**bin_exp)) if regular or
205
// floor(log10(3/4 * 2**bin_exp)) otherwise, without branching.
206
0
const fn compute_dec_exp(bin_exp: i32, regular: bool) -> i32 {
207
0
    debug_assert!(bin_exp >= -1334 && bin_exp <= 2620);
208
    // log10_3_over_4_sig = -log10(3/4) * 2**log10_2_exp rounded to a power of 2
209
    const LOG10_3_OVER_4_SIG: i32 = 131_072;
210
    // log10_2_sig = round(log10(2) * 2**log10_2_exp)
211
    const LOG10_2_SIG: i32 = 315_653;
212
    const LOG10_2_EXP: i32 = 20;
213
0
    (bin_exp * LOG10_2_SIG - !regular as i32 * LOG10_3_OVER_4_SIG) >> LOG10_2_EXP
214
0
}
215
216
trait FloatTraits: traits::Float {
217
    // Note: Rust port uses wider fixed-notation ranges than upstream.
218
    const FIXED_DEC_EXP: RangeInclusive<i32>;
219
220
    const NUM_BITS: i32;
221
    const NUM_SIG_BITS: i32 = Self::MANTISSA_DIGITS as i32 - 1;
222
    const NUM_EXP_BITS: i32 = Self::NUM_BITS - Self::NUM_SIG_BITS - 1;
223
    const EXP_MASK: i32 = (1 << Self::NUM_EXP_BITS) - 1;
224
    const EXP_BIAS: i32 = (1 << (Self::NUM_EXP_BITS - 1)) - 1;
225
    const EXP_OFFSET: i32 = Self::EXP_BIAS + Self::NUM_SIG_BITS;
226
227
    type SigType: traits::UInt;
228
    const IMPLICIT_BIT: Self::SigType;
229
230
    type DecDigitsType: Copy;
231
232
    #[cfg(any(
233
        all(target_arch = "aarch64", target_feature = "neon", not(miri)),
234
        all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
235
    ))]
236
    type DecUnshuffledType;
237
238
    fn to_bits(self) -> Self::SigType;
239
240
0
    fn is_negative(bits: Self::SigType) -> bool {
241
0
        (bits >> (Self::NUM_BITS - 1)) != Self::SigType::from(0)
242
0
    }
Unexecuted instantiation: <f64 as zmij::FloatTraits>::is_negative
Unexecuted instantiation: <f32 as zmij::FloatTraits>::is_negative
243
244
0
    fn get_sig(bits: Self::SigType) -> Self::SigType {
245
0
        bits & (Self::IMPLICIT_BIT - Self::SigType::from(1))
246
0
    }
Unexecuted instantiation: <f64 as zmij::FloatTraits>::get_sig
Unexecuted instantiation: <f32 as zmij::FloatTraits>::get_sig
247
248
0
    fn get_exp(bits: Self::SigType) -> i64 {
249
0
        (bits << 1u8 >> (Self::NUM_SIG_BITS + 1)).into() as i64
250
0
    }
Unexecuted instantiation: <f64 as zmij::FloatTraits>::get_exp
Unexecuted instantiation: <f32 as zmij::FloatTraits>::get_exp
251
252
    // Converts a significand to a string, removing trailing zeros. value has up
253
    // to 17 decimal digits (16-17 for normals) for f64 and up to 9 digits (8-9
254
    // for normals) for f32.
255
    fn to_digits(value: u64, d: &Data) -> DecDigits<Self>;
256
257
    unsafe fn write_exp_float_simd(
258
        buffer: *mut u8,
259
        dig: &DecDigits<Self>,
260
        last_digit: i32,
261
        has_last_digit: bool,
262
        has_extra_digit: bool,
263
        exp_data: u64,
264
        d: &Data,
265
    ) -> *mut u8;
266
}
267
268
impl FloatTraits for f32 {
269
    // Upstream uses -4..=6.
270
    const FIXED_DEC_EXP: RangeInclusive<i32> = -6..=12;
271
272
    const NUM_BITS: i32 = 32;
273
    const IMPLICIT_BIT: u32 = 1 << Self::NUM_SIG_BITS;
274
275
    type SigType = u32;
276
277
    type DecDigitsType = u64;
278
279
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
280
    type DecUnshuffledType = uint8x16_t;
281
    #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
282
    type DecUnshuffledType = __m128i;
283
284
    #[inline]
285
0
    fn to_bits(self) -> Self::SigType {
286
0
        self.to_bits()
287
0
    }
288
289
    #[inline]
290
0
    fn to_digits(value: u64, d: &Data) -> DecDigits<Self> {
291
0
        to_digits_32(value, d)
292
0
    }
293
294
    #[inline]
295
0
    unsafe fn write_exp_float_simd(
296
0
        buffer: *mut u8,
297
0
        dig: &DecDigits<Self>,
298
0
        last_digit: i32,
299
0
        has_last_digit: bool,
300
0
        has_extra_digit: bool,
301
0
        exp_data: u64,
302
0
        d: &Data,
303
0
    ) -> *mut u8 {
304
        unsafe {
305
0
            write_exp_float_simd_32(
306
0
                buffer,
307
0
                dig,
308
0
                last_digit,
309
0
                has_last_digit,
310
0
                has_extra_digit,
311
0
                exp_data,
312
0
                d,
313
            )
314
        }
315
0
    }
316
}
317
318
impl FloatTraits for f64 {
319
    // Upstream uses -4..=15.
320
    const FIXED_DEC_EXP: RangeInclusive<i32> = -5..=15;
321
322
    const NUM_BITS: i32 = 64;
323
    const IMPLICIT_BIT: u64 = 1 << Self::NUM_SIG_BITS;
324
325
    type SigType = u64;
326
327
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
328
    type DecDigitsType = uint16x8_t;
329
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
330
    type DecDigitsType = __m128i;
331
    #[cfg(not(any(
332
        all(target_arch = "aarch64", target_feature = "neon", not(miri)),
333
        all(target_arch = "x86_64", target_feature = "sse2", not(miri)),
334
    )))]
335
    type DecDigitsType = [u64; 2];
336
337
    #[cfg(any(
338
        all(target_arch = "aarch64", target_feature = "neon", not(miri)),
339
        all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
340
    ))]
341
    type DecUnshuffledType = ();
342
343
    #[inline]
344
0
    fn to_bits(self) -> Self::SigType {
345
0
        self.to_bits()
346
0
    }
347
348
    #[inline]
349
0
    fn to_digits(value: u64, d: &Data) -> DecDigits<Self> {
350
0
        to_digits_64(value, d)
351
0
    }
352
353
    #[inline]
354
0
    unsafe fn write_exp_float_simd(
355
0
        _buffer: *mut u8,
356
0
        _dig: &DecDigits<Self>,
357
0
        _last_digit: i32,
358
0
        _has_last_digit: bool,
359
0
        _has_extra_digit: bool,
360
0
        _exp_data: u64,
361
0
        _d: &Data,
362
0
    ) -> *mut u8 {
363
0
        ptr::null_mut()
364
0
    }
365
}
366
367
#[rustfmt::skip]
368
const POW10_MINOR: [u64; 28] = [
369
    0x8000000000000000, 0xa000000000000000, 0xc800000000000000,
370
    0xfa00000000000000, 0x9c40000000000000, 0xc350000000000000,
371
    0xf424000000000000, 0x9896800000000000, 0xbebc200000000000,
372
    0xee6b280000000000, 0x9502f90000000000, 0xba43b74000000000,
373
    0xe8d4a51000000000, 0x9184e72a00000000, 0xb5e620f480000000,
374
    0xe35fa931a0000000, 0x8e1bc9bf04000000, 0xb1a2bc2ec5000000,
375
    0xde0b6b3a76400000, 0x8ac7230489e80000, 0xad78ebc5ac620000,
376
    0xd8d726b7177a8000, 0x878678326eac9000, 0xa968163f0a57b400,
377
    0xd3c21bcecceda100, 0x84595161401484a0, 0xa56fa5b99019a5c8,
378
    0xcecb8f27f4200f3a,
379
];
380
381
#[rustfmt::skip]
382
const POW10_MAJOR: [uint128; 23] = [
383
    uint128 { hi: 0xaf8e5410288e1b6f, lo: 0x07ecf0ae5ee44dda }, // -303
384
    uint128 { hi: 0xb1442798f49ffb4a, lo: 0x99cd11cfdf41779d }, // -275
385
    uint128 { hi: 0xb2fe3f0b8599ef07, lo: 0x861fa7e6dcb4aa15 }, // -247
386
    uint128 { hi: 0xb4bca50b065abe63, lo: 0x0fed077a756b53aa }, // -219
387
    uint128 { hi: 0xb67f6455292cbf08, lo: 0x1a3bc84c17b1d543 }, // -191
388
    uint128 { hi: 0xb84687c269ef3bfb, lo: 0x3d5d514f40eea742 }, // -163
389
    uint128 { hi: 0xba121a4650e4ddeb, lo: 0x92f34d62616ce413 }, // -135
390
    uint128 { hi: 0xbbe226efb628afea, lo: 0x890489f70a55368c }, // -107
391
    uint128 { hi: 0xbdb6b8e905cb600f, lo: 0x5400e987bbc1c921 }, //  -79
392
    uint128 { hi: 0xbf8fdb78849a5f96, lo: 0xde98520472bdd034 }, //  -51
393
    uint128 { hi: 0xc16d9a0095928a27, lo: 0x75b7053c0f178294 }, //  -23
394
    uint128 { hi: 0xc350000000000000, lo: 0x0000000000000000 }, //    5
395
    uint128 { hi: 0xc5371912364ce305, lo: 0x6c28000000000000 }, //   33
396
    uint128 { hi: 0xc722f0ef9d80aad6, lo: 0x424d3ad2b7b97ef6 }, //   61
397
    uint128 { hi: 0xc913936dd571c84c, lo: 0x03bc3a19cd1e38ea }, //   89
398
    uint128 { hi: 0xcb090c8001ab551c, lo: 0x5cadf5bfd3072cc6 }, //  117
399
    uint128 { hi: 0xcd036837130890a1, lo: 0x36dba887c37a8c10 }, //  145
400
    uint128 { hi: 0xcf02b2c21207ef2e, lo: 0x94f967e45e03f4bc }, //  173
401
    uint128 { hi: 0xd106f86e69d785c7, lo: 0xe13336d701beba52 }, //  201
402
    uint128 { hi: 0xd31045a8341ca07c, lo: 0x1ede48111209a051 }, //  229
403
    uint128 { hi: 0xd51ea6fa85785631, lo: 0x552a74227f3ea566 }, //  257
404
    uint128 { hi: 0xd732290fbacaf133, lo: 0xa97c177947ad4096 }, //  285
405
    uint128 { hi: 0xd94ad8b1c7380874, lo: 0x18375281ae7822bc }, //  313
406
];
407
408
#[rustfmt::skip]
409
const POW10_FIXUPS: [u32; 20] = [
410
    0x0a4e363f, 0x00001840, 0x00006400, 0x24200040, 0x00000000,
411
    0x0c000000, 0x82c81380, 0x5e4ce01f, 0xd730f60f, 0x0000001b,
412
    0x00000000, 0xcdf7fffc, 0x6e8201d8, 0x40cd3fd1, 0xdb642501,
413
    0x00000d0d, 0x14042400, 0x53713840, 0x11781db4, 0x00000000,
414
];
415
416
// 128-bit significands of powers of 10 rounded down.
417
#[repr(C, align(64))]
418
struct Pow10SignificandTable {
419
    data: [u64; if Self::COMPRESS {
420
        0
421
    } else {
422
        Self::NUM_POW10S * 2
423
    }],
424
}
425
426
impl Pow10SignificandTable {
427
    const COMPRESS: bool = cfg!(opt_level = "s");
428
    const SPLIT_TABLES: bool = !Self::COMPRESS && cfg!(target_arch = "aarch64");
429
    const NUM_POW10S: usize = 618;
430
431
    // Computes the 128-bit significand of 10**i using method by Dougall Johnson.
432
    #[inline]
433
0
    const fn compute(i: u32) -> uint128 {
434
        const STRIDE: u32 = POW10_MINOR.len() as u32;
435
0
        let m = unsafe { *POW10_MINOR.as_ptr().add(((i + 10) % STRIDE) as usize) };
436
0
        let h = unsafe { *POW10_MAJOR.as_ptr().add(((i + 10) / STRIDE) as usize) };
437
438
0
        let h1 = umul128_hi64(h.lo, m);
439
440
0
        let c0 = h.lo.wrapping_mul(m);
441
0
        let c1 = h1.wrapping_add(h.hi.wrapping_mul(m));
442
0
        let c2 = (c1 < h1) as u64 + umul128_hi64(h.hi, m);
443
444
0
        let mut result = if (c2 >> 63) != 0 {
445
0
            uint128 { hi: c2, lo: c1 }
446
        } else {
447
0
            uint128 {
448
0
                hi: (c2 << 1) | (c1 >> 63),
449
0
                lo: (c1 << 1) | (c0 >> 63),
450
0
            }
451
        };
452
0
        result.lo -=
453
0
            ((unsafe { *POW10_FIXUPS.as_ptr().add((i >> 5) as usize) } >> (i & 31)) & 1) as u64;
454
0
        result
455
0
    }
456
457
0
    const fn new() -> Self {
458
0
        let mut data = [0; if Self::COMPRESS {
459
0
            0
460
0
        } else {
461
0
            Self::NUM_POW10S * 2
462
0
        }];
463
464
0
        let mut i = 0;
465
0
        while i < Self::NUM_POW10S && !Self::COMPRESS {
466
0
            let result = Self::compute(i as u32);
467
0
            if Self::SPLIT_TABLES {
468
0
                data[Self::NUM_POW10S - i - 1] = result.hi;
469
0
                data[Self::NUM_POW10S * 2 - i - 1] = result.lo;
470
0
            } else {
471
0
                data[i * 2] = result.hi;
472
0
                data[i * 2 + 1] = result.lo;
473
0
            }
474
0
            i += 1;
475
        }
476
477
0
        Pow10SignificandTable { data }
478
0
    }
479
480
    #[inline]
481
0
    unsafe fn get_unchecked(&self, dec_exp: i32) -> uint128 {
482
        const DEC_EXP_MIN: i32 = -293;
483
0
        let i = dec_exp - DEC_EXP_MIN;
484
0
        if Self::COMPRESS {
485
0
            return Self::compute(i as u32);
486
0
        }
487
0
        if !Self::SPLIT_TABLES {
488
0
            let p = unsafe { self.data.as_ptr().add((i * 2) as usize) };
489
0
            return uint128 {
490
0
                hi: unsafe { *p },
491
0
                lo: unsafe { *p.add(1) },
492
0
            };
493
0
        }
494
495
        unsafe {
496
            // The caller passes -e - 1 as dec_exp, so ~dec_exp recovers e.
497
            // Picking the base so that e itself is the index lets both loads
498
            // share sxtw addressing.
499
            #[cfg_attr(
500
                not(all(any(target_arch = "x86_64", target_arch = "aarch64"), not(miri))),
501
                allow(unused_mut)
502
            )]
503
0
            let mut p = self
504
0
                .data
505
0
                .as_ptr()
506
0
                .offset(Self::NUM_POW10S as isize + DEC_EXP_MIN as isize);
507
            #[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), not(miri)))]
508
0
            asm!("/*{0}*/", inout(reg) p);
509
0
            uint128 {
510
0
                hi: *p.offset(!(dec_exp as isize)),
511
0
                lo: *p.offset(!(dec_exp as isize) + Self::NUM_POW10S as isize),
512
0
            }
513
        }
514
0
    }
515
516
    #[cfg(test)]
517
    fn get(&self, dec_exp: i32) -> uint128 {
518
        const DEC_EXP_MIN: i32 = -292;
519
        assert!((DEC_EXP_MIN..DEC_EXP_MIN + Self::NUM_POW10S as i32).contains(&dec_exp));
520
        unsafe { self.get_unchecked(dec_exp) }
521
    }
522
}
523
524
// Computes a shift so that, after scaling by a power of 10, the intermediate
525
// result always has a fixed 128-bit fractional part (for double).
526
//
527
// Different binary exponents can map to the same decimal exponent, but place
528
// the decimal point at different bit positions. The shift compensates for this.
529
//
530
// For example, both 3 * 2**59 and 3 * 2**60 have dec_exp = 2, but dividing by
531
// 10^dec_exp puts the decimal point in different bit positions:
532
//   3 * 2**59 / 100 = 1.72...e+16  (needs shift = 1 + 1)
533
//   3 * 2**60 / 100 = 3.45...e+16  (needs shift = 2 + 1)
534
#[inline]
535
0
const fn compute_exp_shift(bin_exp: i32, dec_exp: i32) -> u8 {
536
0
    debug_assert!(dec_exp >= -350 && dec_exp <= 350);
537
    // log2_pow10_sig = round(log2(10) * 2**log2_pow10_exp) + 1
538
    const LOG2_POW10_SIG: i32 = 217_707;
539
    const LOG2_POW10_EXP: i32 = 16;
540
    // pow10_bin_exp = floor(log2(10**-dec_exp))
541
0
    let pow10_bin_exp = (-dec_exp * LOG2_POW10_SIG) >> LOG2_POW10_EXP;
542
    // pow10 = ((pow10_hi << 64) | pow10_lo) * 2**(pow10_bin_exp - 127)
543
0
    (bin_exp + pow10_bin_exp + 1) as u8
544
0
}
545
546
struct ExpShiftTable {
547
    data: [u8; if Self::ENABLE {
548
        f64::EXP_MASK as usize + 1
549
    } else {
550
        0
551
    }],
552
}
553
554
impl ExpShiftTable {
555
    const ENABLE: bool = cfg!(not(opt_level = "s"));
556
    // extra_shift must be >= 3 to keep shift non-negative and <= 11 to fit the
557
    // significand into 64 bits after the shift.
558
    const EXTRA_SHIFT: usize = 6;
559
560
0
    const fn new() -> Self {
561
0
        let mut data = [0u8; if Self::ENABLE {
562
0
            f64::EXP_MASK as usize + 1
563
0
        } else {
564
0
            0
565
0
        }];
566
567
0
        let mut raw_exp = 0;
568
0
        while raw_exp < data.len() && Self::ENABLE {
569
0
            let mut bin_exp = raw_exp as i32 - f64::EXP_OFFSET;
570
0
            if raw_exp == 0 {
571
0
                bin_exp += 1;
572
0
            }
573
0
            let dec_exp = compute_dec_exp(bin_exp, true);
574
0
            data[raw_exp] =
575
0
                compute_exp_shift(bin_exp, dec_exp + 1).wrapping_add(Self::EXTRA_SHIFT as u8);
576
0
            raw_exp += 1;
577
        }
578
579
0
        ExpShiftTable { data }
580
0
    }
581
}
582
583
// An optional table of precomputed exponent strings for exponential notation.
584
// Each entry packs "e+dd" or "e+ddd" into a u64 with the length in byte 7.
585
struct ExpStringTable {
586
    data: [u64; if Self::ENABLE {
587
        (f64::MAX_10_EXP - Self::MIN_DEC_EXP + 1) as usize
588
    } else {
589
        0
590
    }],
591
}
592
593
impl ExpStringTable {
594
    const ENABLE: bool = cfg!(not(opt_level = "s"));
595
    const MIN_DEC_EXP: i32 = f64::MIN_10_EXP - f64::MAX_DIGITS10 as i32;
596
    const OFFSET: i32 = -Self::MIN_DEC_EXP;
597
598
0
    const fn new() -> Self {
599
0
        let mut data = [0u64; if Self::ENABLE {
600
0
            (f64::MAX_10_EXP - Self::MIN_DEC_EXP + 1) as usize
601
0
        } else {
602
0
            0
603
0
        }];
604
605
0
        let mut e = Self::MIN_DEC_EXP;
606
0
        while e <= f64::MAX_10_EXP && Self::ENABLE {
607
0
            let abs_e = e.unsigned_abs() as u64;
608
0
            let mut val = abs_e % 10 + b'0' as u64;
609
0
            if abs_e >= 10 {
610
0
                val = (val << 8) | (abs_e / 10 % 10 + b'0' as u64);
611
0
            }
612
0
            if abs_e >= 100 {
613
0
                val = (val << 8) | (abs_e / 100 + b'0' as u64);
614
0
            }
615
0
            let len = 3 + (abs_e >= 10) as u64 + (abs_e >= 100) as u64;
616
0
            data[(e + Self::OFFSET) as usize] = (len << 48)
617
0
                | (val << 16)
618
0
                | (if e >= 0 { b'+' as u64 } else { b'-' as u64 } << 8)
619
0
                | b'e' as u64;
620
0
            e += 1;
621
        }
622
623
0
        ExpStringTable { data }
624
0
    }
625
}
626
627
// Shuffle vectors to build strings for exponential notation.
628
//
629
// Byte positions in the source register assembled by write_exp_float_simd:
630
//   bytes [0, exp_pos):              BCD ASCII digits (reversed)
631
//   bytes [exp_pos, exp_pos + 4):    exponent string "e±NN"
632
//   byte  last_digit_pos:            rounded last digit
633
//   byte  point_pos:                 '.'
634
//
635
// The shuffle length (max 14) is stored in byte 15; the corresponding output
636
// byte is past the string and ignored by the caller.
637
#[repr(C, align(16))]
638
struct ExpFloatShuffleTable {
639
    data: [u8; if Self::ENABLE { 32 * 16 } else { 0 }],
640
}
641
642
struct ExpFloatShuffleTableEntry {
643
    #[cfg_attr(
644
        not(any(
645
            all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
646
            all(target_arch = "aarch64", target_feature = "neon", not(miri)),
647
        )),
648
        allow(dead_code)
649
    )]
650
    shuffle: *const u8,
651
    length: u8,
652
}
653
654
impl ExpFloatShuffleTable {
655
    const ENABLE: bool = cfg!(any(
656
        all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
657
        all(target_arch = "aarch64", target_feature = "neon", not(miri)),
658
    )) && ExpStringTable::ENABLE;
659
660
    const EXP_POS: u8 = 8;
661
    const LAST_DIGIT_POS: u8 = 12;
662
    const POINT_POS: u8 = 13;
663
664
0
    unsafe fn get_entry(
665
0
        &self,
666
0
        num_digits: i32,
667
0
        has_last_digit: bool,
668
0
        has_extra_digit: bool,
669
0
    ) -> ExpFloatShuffleTableEntry {
670
0
        let idx = (num_digits - 1) * 4 + i32::from(has_last_digit) * 2 + i32::from(has_extra_digit);
671
0
        ExpFloatShuffleTableEntry {
672
0
            shuffle: unsafe { self.data.as_ptr().add(idx as usize * 16) },
673
0
            length: *unsafe { self.data.get_unchecked(idx as usize * 16 + 15) },
674
0
        }
675
0
    }
676
677
0
    const fn new() -> Self {
678
0
        let mut data = [0u8; if Self::ENABLE { 32 * 16 } else { 0 }];
679
680
0
        let mut idx = 0;
681
0
        while idx < 32 && Self::ENABLE {
682
0
            let num_digits = (idx >> 2) + 1;
683
0
            let has_last_digit = ((idx >> 1) & 1) != 0;
684
0
            let has_extra_digit = (idx & 1) != 0;
685
686
0
            let out = idx * 16;
687
0
            let mut i = 0;
688
0
            while i < 16 {
689
0
                data[out + i] = 0x80; // shuffle high bit: output 0
690
0
                i += 1;
691
0
            }
692
0
            let leading_digit_pos = if has_extra_digit { 7 } else { 6 };
693
0
            let mut length = 0;
694
0
            if has_last_digit {
695
                // Always 8 BCD chars in the significand plus a last-digit char;
696
                // for !has_extra_digit the leading '0' of the 8-digit padded
697
                // BCD is shown.
698
0
                data[out + length] = leading_digit_pos;
699
0
                length += 1;
700
0
                data[out + length] = Self::POINT_POS;
701
0
                length += 1;
702
0
                let mut i = leading_digit_pos - 1;
703
                loop {
704
0
                    data[out + length] = i;
705
0
                    length += 1;
706
0
                    if i == 0 {
707
0
                        break;
708
0
                    }
709
0
                    i -= 1;
710
                }
711
0
                data[out + length] = Self::LAST_DIGIT_POS;
712
0
                length += 1;
713
            } else {
714
0
                length = num_digits + has_extra_digit as usize;
715
                // Drop the '.' for single-digit output: "5e+02", not "5.0e+02".
716
0
                if length == 2 {
717
0
                    length = 1;
718
0
                }
719
0
                data[out] = leading_digit_pos;
720
0
                data[out + 1] = Self::POINT_POS;
721
0
                let mut i = 2;
722
0
                while i < length {
723
0
                    data[out + i] = leading_digit_pos + 1 - i as u8;
724
0
                    i += 1;
725
0
                }
726
            }
727
0
            let mut i = 0;
728
0
            while i < 4 {
729
0
                data[out + length] = Self::EXP_POS + i;
730
0
                length += 1;
731
0
                i += 1;
732
0
            }
733
0
            data[out + 15] = length as u8;
734
0
            idx += 1;
735
        }
736
737
0
        ExpFloatShuffleTable { data }
738
0
    }
739
}
740
741
#[cfg(any(
742
    not(any(
743
        all(target_arch = "aarch64", target_feature = "neon", not(miri)),
744
        all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
745
    )),
746
    all(test, target_endian = "little"),
747
))]
748
#[cfg_attr(feature = "no-panic", no_panic)]
749
0
fn count_trailing_nonzeros(x: u64) -> usize {
750
    // We count the number of bytes until there are only zeros left.
751
    // The code is equivalent to
752
    //    8 - x.leading_zeros() / 8
753
    // but if the BSR instruction is emitted (as gcc on x64 does with default
754
    // settings), subtracting the constant before dividing allows the compiler
755
    // to combine it with the subtraction which it inserts due to BSR counting
756
    // in the opposite direction.
757
    //
758
    // Additionally, the BSR instruction requires a zero check. Since the high
759
    // bit is unused we can avoid the zero check by shifting the datum left by
760
    // one and inserting a sentinel bit at the end. This can be faster than the
761
    // automatically inserted range check.
762
0
    (70 - ((x.to_le() << 1) | 1).leading_zeros() as usize) / 8
763
0
}
764
765
// Align data since unaligned access may be slower when crossing a
766
// hardware-specific boundary.
767
#[repr(C, align(2))]
768
struct Digits2([u8; 200]);
769
770
static DIGITS2: Digits2 = Digits2(
771
    *b"0001020304050607080910111213141516171819\
772
       2021222324252627282930313233343536373839\
773
       4041424344454647484950515253545556575859\
774
       6061626364656667686970717273747576777879\
775
       8081828384858687888990919293949596979899",
776
);
777
778
// Converts value in the range [0, 100) to a string. GCC generates a bit better
779
// code when value is pointer-size (https://www.godbolt.org/z/5fEPMT1cc).
780
#[cfg_attr(feature = "no-panic", no_panic)]
781
0
unsafe fn digits2(value: usize) -> &'static u16 {
782
0
    debug_assert!(value < 100);
783
784
    #[allow(clippy::cast_ptr_alignment)]
785
    unsafe {
786
0
        &*DIGITS2.0.as_ptr().cast::<u16>().add(value)
787
    }
788
0
}
789
790
const DIV10K_EXP: i32 = 40;
791
const DIV10K_SIG: u32 = ((1u64 << DIV10K_EXP) / 10000 + 1) as u32;
792
const NEG10K: u32 = ((1u64 << 32) - 10000) as u32;
793
794
const DIV100_EXP: i32 = 19;
795
const DIV100_SIG: u32 = (1 << DIV100_EXP) / 100 + 1;
796
#[cfg(not(all(
797
    target_arch = "x86_64",
798
    target_feature = "sse2",
799
    not(target_feature = "sse4.1"),
800
    not(miri)
801
)))]
802
const NEG100: u32 = (1 << 16) - 100;
803
804
#[cfg(not(any(
805
    all(target_arch = "x86_64", target_feature = "sse2", not(miri)),
806
    all(target_arch = "aarch64", target_feature = "neon", not(miri)),
807
)))]
808
const DIV10_EXP: i32 = 10;
809
#[cfg(not(any(
810
    all(target_arch = "x86_64", target_feature = "sse2", not(miri)),
811
    all(target_arch = "aarch64", target_feature = "neon", not(miri)),
812
)))]
813
const DIV10_SIG: u32 = (1 << DIV10_EXP) / 10 + 1;
814
#[cfg(not(all(target_arch = "x86_64", target_feature = "sse2", not(miri))))]
815
const NEG10: u32 = (1 << 8) - 10;
816
817
const ZEROS: u64 = 0x0101010101010101 * b'0' as u64;
818
819
#[repr(C, align(64))]
820
struct Data {
821
    threshold: AArch64Mem<1_000_000_000_000_000>,
822
    // +6 is needed for boundary cases found by verify.py.
823
    biased_half: AArch64Mem<{ (1 << 63) + 6 }>,
824
825
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
826
    mul_const: u64,
827
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
828
    hundred_million: u64,
829
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
830
    multipliers32: int32x4_t,
831
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
832
    multipliers16: int16x8_t,
833
834
    // Ordered so that the values used to format floats fit in a single cache
835
    // line.
836
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
837
    div100: u128,
838
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
839
    div10: u128,
840
    #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
841
    neg100: u128,
842
    #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
843
    neg10: u128,
844
    #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
845
    bswap: u128,
846
    #[cfg(all(
847
        target_arch = "x86_64",
848
        target_feature = "sse2",
849
        not(target_feature = "sse4.1"),
850
        not(miri)
851
    ))]
852
    hundred: u128,
853
    #[cfg(all(
854
        target_arch = "x86_64",
855
        target_feature = "sse2",
856
        not(target_feature = "sse4.1"),
857
        not(miri)
858
    ))]
859
    moddiv10: u128,
860
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
861
    div10k: u128,
862
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
863
    neg10k: u128,
864
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
865
    zeros: u128,
866
867
    exp_shifts: ExpShiftTable,
868
    exp_strings: ExpStringTable,
869
    pow10_significands: Pow10SignificandTable,
870
    exp_float_shuffles: ExpFloatShuffleTable,
871
}
872
873
impl Data {
874
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
875
0
    const fn splat64(x: u64) -> u128 {
876
0
        ((x as u128) << 64) | x as u128
877
0
    }
878
879
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
880
0
    const fn splat32(x: u32) -> u128 {
881
0
        Self::splat64(((x as u64) << 32) | x as u64)
882
0
    }
883
884
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
885
0
    const fn splat16(x: u16) -> u128 {
886
0
        Self::splat32(((x as u32) << 16) | x as u32)
887
0
    }
888
889
    #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
890
    const fn pack8(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8, g: u8, h: u8) -> u64 {
891
        ((h as u64) << 56)
892
            | ((g as u64) << 48)
893
            | ((f as u64) << 40)
894
            | ((e as u64) << 32)
895
            | ((d as u64) << 24)
896
            | ((c as u64) << 16)
897
            | ((b as u64) << 8)
898
            | a as u64
899
    }
900
901
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
902
    const NEG10K: i32 = 0x10000 - 10000;
903
}
904
905
static STATIC_DATA: Data = Data {
906
    threshold: AArch64Mem::new(),
907
    biased_half: AArch64Mem::new(),
908
909
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
910
    mul_const: 0xabcc77118461cefd,
911
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
912
    hundred_million: 100000000,
913
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
914
    multipliers32: unsafe {
915
        mem::transmute::<[i32; 4], int32x4_t>([
916
            DIV10K_SIG as i32,
917
            Data::NEG10K,
918
            (DIV100_SIG << 12) as i32,
919
            NEG100 as i32,
920
        ])
921
    },
922
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
923
    multipliers16: unsafe {
924
        mem::transmute::<[i16; 8], int16x8_t>([0xce0, NEG10 as i16, 0, 0, 0, 0, 0, 0])
925
    },
926
927
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
928
    div100: Data::splat32(DIV100_SIG),
929
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
930
    div10: Data::splat16(((1u32 << 16) / 10 + 1) as u16),
931
    #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
932
    neg100: Data::splat32(NEG100),
933
    #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
934
    neg10: Data::splat16((1 << 8) - 10),
935
    #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
936
    bswap: Data::pack8(15, 14, 13, 12, 11, 10, 9, 8) as u128
937
        | (Data::pack8(7, 6, 5, 4, 3, 2, 1, 0) as u128) << 64,
938
    #[cfg(all(
939
        target_arch = "x86_64",
940
        target_feature = "sse2",
941
        not(target_feature = "sse4.1"),
942
        not(miri)
943
    ))]
944
    hundred: Data::splat32(100),
945
    #[cfg(all(
946
        target_arch = "x86_64",
947
        target_feature = "sse2",
948
        not(target_feature = "sse4.1"),
949
        not(miri)
950
    ))]
951
    moddiv10: Data::splat16(10 * (1 << 8) - 1),
952
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
953
    div10k: Data::splat64(DIV10K_SIG as u64),
954
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
955
    neg10k: Data::splat64(NEG10K as u64),
956
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
957
    zeros: Data::splat64(ZEROS),
958
959
    exp_shifts: ExpShiftTable::new(),
960
    exp_strings: ExpStringTable::new(),
961
    pow10_significands: Pow10SignificandTable::new(),
962
    exp_float_shuffles: ExpFloatShuffleTable::new(),
963
};
964
965
// Converts four numbers < 10000, one in each 32-bit lane, to BCD digits.
966
#[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
967
#[cfg_attr(feature = "no-panic", no_panic)]
968
fn to_bcd_4x4(mut efgh_abcd_mnop_ijkl: int32x4_t, d: &Data) -> uint8x16_t {
969
    unsafe {
970
        // Compiler barrier, or clang breaks the subsequent MLA into UADDW +
971
        // MUL.
972
        asm!("/*{:v}*/", inout(vreg) efgh_abcd_mnop_ijkl);
973
974
        let ef_ab_mn_ij: int32x4_t = vqdmulhq_n_s32(
975
            efgh_abcd_mnop_ijkl,
976
            mem::transmute::<int32x4_t, [i32; 4]>(d.multipliers32)[2],
977
        );
978
        let gh_ef_cd_ab_op_mn_kl_ij: int16x8_t = vreinterpretq_s16_s32(vmlaq_n_s32(
979
            efgh_abcd_mnop_ijkl,
980
            ef_ab_mn_ij,
981
            mem::transmute::<int32x4_t, [i32; 4]>(d.multipliers32)[3],
982
        ));
983
        let high_10s: int16x8_t = vqdmulhq_n_s16(
984
            gh_ef_cd_ab_op_mn_kl_ij,
985
            mem::transmute::<int16x8_t, [i16; 8]>(d.multipliers16)[0],
986
        );
987
        vreinterpretq_u8_s16(vmlaq_n_s16(
988
            gh_ef_cd_ab_op_mn_kl_ij,
989
            high_10s,
990
            mem::transmute::<int16x8_t, [i16; 8]>(d.multipliers16)[1],
991
        ))
992
    }
993
}
994
995
// An optimized version for NEON by Dougall Johnson.
996
#[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
997
#[cfg_attr(feature = "no-panic", no_panic)]
998
#[inline]
999
fn to_unshuffled_digits(value: u64, d: &Data) -> uint8x16_t {
1000
    let mut hundred_million = d.hundred_million;
1001
1002
    // Compiler barrier, or clang narrows the load to 32-bit and unpairs it.
1003
    unsafe {
1004
        asm!("/*{0}*/", inout(reg) hundred_million);
1005
    }
1006
1007
    // abcdefgh = value / 100000000, ijklmnop = value % 100000000.
1008
    let abcdefgh = (umul128(value, d.mul_const) >> 90) as u64;
1009
    let ijklmnop = value - abcdefgh * hundred_million;
1010
1011
    unsafe {
1012
        let ijklmnop_abcdefgh_64: uint64x1_t =
1013
            mem::transmute::<u64, uint64x1_t>((ijklmnop << 32) | abcdefgh);
1014
        let abcdefgh_ijklmnop: int32x2_t = vreinterpret_s32_u64(ijklmnop_abcdefgh_64);
1015
1016
        let abcd_ijkl: int32x2_t = vreinterpret_s32_u32(vshr_n_u32(
1017
            vreinterpret_u32_s32(vqdmulh_n_s32(
1018
                abcdefgh_ijklmnop,
1019
                mem::transmute::<int32x4_t, [i32; 4]>(d.multipliers32)[0],
1020
            )),
1021
            9,
1022
        ));
1023
        let efgh_abcd_mnop_ijkl_32: int32x2_t = vmla_n_s32(
1024
            abcdefgh_ijklmnop,
1025
            abcd_ijkl,
1026
            mem::transmute::<int32x4_t, [i32; 4]>(d.multipliers32)[1],
1027
        );
1028
1029
        let efgh_abcd_mnop_ijkl: int32x4_t =
1030
            vreinterpretq_s32_u32(vshll_n_u16(vreinterpret_u16_s32(efgh_abcd_mnop_ijkl_32), 0));
1031
1032
        to_bcd_4x4(efgh_abcd_mnop_ijkl, d)
1033
    }
1034
}
1035
1036
// Converts four numbers < 10000, one in each 32-bit lane, to BCD digits.
1037
// Digits in each 32-bit lane will be in order for SSE2, reversed for SSE4.1.
1038
#[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
1039
#[cfg_attr(feature = "no-panic", no_panic)]
1040
0
fn to_bcd_4x4(y: __m128i, d: &Data) -> __m128i {
1041
    unsafe {
1042
0
        let div100 = _mm_load_si128(ptr::addr_of!(d.div100).cast::<__m128i>());
1043
0
        let div10 = _mm_load_si128(ptr::addr_of!(d.div10).cast::<__m128i>());
1044
1045
        #[cfg(target_feature = "sse4.1")]
1046
        {
1047
            let neg100 = _mm_load_si128(ptr::addr_of!(d.neg100).cast::<__m128i>());
1048
            let neg10 = _mm_load_si128(ptr::addr_of!(d.neg10).cast::<__m128i>());
1049
1050
            // _mm_mullo_epi32 is SSE 4.1
1051
            let z: __m128i = _mm_add_epi64(
1052
                y,
1053
                _mm_mullo_epi32(neg100, _mm_srli_epi32(_mm_mulhi_epu16(y, div100), 3)),
1054
            );
1055
            _mm_add_epi64(z, _mm_mullo_epi16(neg10, _mm_mulhi_epu16(z, div10)))
1056
        }
1057
1058
        #[cfg(not(target_feature = "sse4.1"))]
1059
        {
1060
0
            let hundred = _mm_load_si128(ptr::addr_of!(d.hundred).cast::<__m128i>());
1061
0
            let moddiv10 = _mm_load_si128(ptr::addr_of!(d.moddiv10).cast::<__m128i>());
1062
1063
0
            let y_div_100: __m128i = _mm_srli_epi16(_mm_mulhi_epu16(y, div100), 3);
1064
0
            let y_mod_100: __m128i = _mm_sub_epi16(y, _mm_mullo_epi16(y_div_100, hundred));
1065
0
            let z: __m128i = _mm_or_si128(_mm_slli_epi32(y_mod_100, 16), y_div_100);
1066
0
            _mm_sub_epi16(
1067
0
                _mm_slli_epi16(z, 8),
1068
0
                _mm_mullo_epi16(moddiv10, _mm_mulhi_epu16(z, div10)),
1069
            )
1070
        }
1071
    }
1072
0
}
1073
1074
#[cfg(not(any(
1075
    all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
1076
    all(target_arch = "aarch64", target_feature = "neon", not(miri)),
1077
)))]
1078
struct BcdResult {
1079
    bcd: u64,
1080
    len: usize,
1081
}
1082
1083
#[cfg(not(any(
1084
    all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
1085
    all(target_arch = "aarch64", target_feature = "neon", not(miri)),
1086
)))]
1087
#[cfg_attr(feature = "no-panic", no_panic)]
1088
0
fn to_bcd8(abcdefgh: u64) -> BcdResult {
1089
    #[cfg(not(all(target_arch = "x86_64", target_feature = "sse2", not(miri))))]
1090
    let bcd = {
1091
        // An optimization from Xiang JunBo.
1092
        // Three steps BCD. Base 10000 -> base 100 -> base 10.
1093
        // div and mod are evaluated simultaneously as, e.g.
1094
        //   (abcdefgh / 10000) << 32 + (abcdefgh % 10000)
1095
        //      == abcdefgh + (2**32 - 10000) * (abcdefgh / 10000)))
1096
        // where the division on the RHS is implemented by the usual multiply + shift
1097
        // trick and the fractional bits are masked away.
1098
        let abcd_efgh =
1099
            abcdefgh + u64::from(NEG10K) * ((abcdefgh * u64::from(DIV10K_SIG)) >> DIV10K_EXP);
1100
        let ab_cd_ef_gh = abcd_efgh
1101
            + u64::from(NEG100)
1102
                * (((abcd_efgh * u64::from(DIV100_SIG)) >> DIV100_EXP) & 0x7f0000007f);
1103
        let a_b_c_d_e_f_g_h = ab_cd_ef_gh
1104
            + u64::from(NEG10)
1105
                * (((ab_cd_ef_gh * u64::from(DIV10_SIG)) >> DIV10_EXP) & 0xf000f000f000f);
1106
        a_b_c_d_e_f_g_h.to_be()
1107
    };
1108
1109
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
1110
0
    let bcd = {
1111
        // Load constants from memory.
1112
0
        let mut d = ptr::addr_of!(STATIC_DATA);
1113
0
        let d = unsafe {
1114
0
            asm!("/*{0}*/", inout(reg) d);
1115
0
            &*d
1116
        };
1117
1118
        // Evaluate the 4-digit limbs and arrange them such that we get a
1119
        // result which is in the correct order.
1120
0
        let abcd_efgh = (abcdefgh << 32)
1121
0
            - ((10000u64 << 32) - 1) * ((abcdefgh * u64::from(DIV10K_SIG)) >> DIV10K_EXP);
1122
0
        let v: __m128i = to_bcd_4x4(_mm_set_epi64x(0, abcd_efgh as i64), d);
1123
0
        (unsafe { _mm_cvtsi128_si64(v) }) as u64
1124
    };
1125
1126
0
    BcdResult {
1127
0
        bcd,
1128
0
        len: count_trailing_nonzeros(bcd),
1129
0
    }
1130
0
}
1131
1132
struct DecDigits<Float: FloatTraits> {
1133
    digits: Float::DecDigitsType,
1134
    // `unshuffled` is the byte-reversed BCD vector used by write_exp_float_simd.
1135
    #[cfg(any(
1136
        all(target_arch = "aarch64", target_feature = "neon", not(miri)),
1137
        all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
1138
    ))]
1139
    unshuffled: Float::DecUnshuffledType,
1140
    num_digits: usize,
1141
}
1142
1143
#[cfg_attr(feature = "no-panic", no_panic)]
1144
#[inline]
1145
0
fn to_digits_64(value: u64, #[allow(unused_variables)] d: &Data) -> DecDigits<f64> {
1146
    #[cfg(not(any(
1147
        all(target_arch = "aarch64", target_feature = "neon", not(miri)),
1148
        all(target_arch = "x86_64", target_feature = "sse2", not(miri)),
1149
    )))]
1150
    {
1151
        let hi = (value / 100_000_000) as u32;
1152
        let lo = (value % 100_000_000) as u32;
1153
        let hi_bcd = to_bcd8(hi as u64);
1154
        if lo == 0 {
1155
            return DecDigits {
1156
                digits: [hi_bcd.bcd + ZEROS, ZEROS],
1157
                num_digits: hi_bcd.len,
1158
            };
1159
        }
1160
        let lo_bcd = to_bcd8(lo as u64);
1161
        DecDigits {
1162
            digits: [hi_bcd.bcd + ZEROS, lo_bcd.bcd + ZEROS],
1163
            num_digits: 8 + lo_bcd.len,
1164
        }
1165
    }
1166
1167
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
1168
    {
1169
        unsafe {
1170
            let unshuffled_digits = to_unshuffled_digits(value, d);
1171
            let digits: uint8x16_t = vrev64q_u8(unshuffled_digits);
1172
            let str: uint16x8_t = vaddq_u16(
1173
                vreinterpretq_u16_u8(digits),
1174
                vreinterpretq_u16_s8(vdupq_n_s8(b'0' as i8)),
1175
            );
1176
            let is_not_zero: uint16x8_t =
1177
                vreinterpretq_u16_u8(vcgtzq_s8(vreinterpretq_s8_u8(digits)));
1178
            let nonzero_mask: u64 =
1179
                vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(is_not_zero, 4)), 0);
1180
            DecDigits {
1181
                digits: str,
1182
                unshuffled: (),
1183
                num_digits: 16 - (nonzero_mask.leading_zeros() as usize >> 2),
1184
            }
1185
        }
1186
    }
1187
1188
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
1189
    {
1190
0
        let hi = (value / 100_000_000) as u32;
1191
0
        let lo = (value % 100_000_000) as u32;
1192
1193
        unsafe {
1194
0
            let div10k = _mm_load_si128(ptr::addr_of!(d.div10k).cast::<__m128i>());
1195
0
            let neg10k = _mm_load_si128(ptr::addr_of!(d.neg10k).cast::<__m128i>());
1196
0
            let x: __m128i = _mm_set_epi64x(i64::from(hi), i64::from(lo));
1197
            #[cfg_attr(target_feature = "sse4.1", allow(unused_mut))]
1198
0
            let mut y: __m128i = _mm_add_epi64(
1199
0
                x,
1200
0
                _mm_mul_epu32(neg10k, _mm_srli_epi64(_mm_mul_epu32(x, div10k), DIV10K_EXP)),
1201
            );
1202
1203
            // Shuffle to ensure correctly ordered result from SSE2 path.
1204
            #[cfg(not(target_feature = "sse4.1"))]
1205
0
            {
1206
0
                y = _mm_shuffle_epi32(y, _MM_SHUFFLE(0, 1, 2, 3));
1207
0
            }
1208
1209
            #[cfg_attr(not(target_feature = "sse4.1"), allow(unused_mut))]
1210
0
            let mut bcd: __m128i = to_bcd_4x4(y, d);
1211
0
            let zeros = _mm_load_si128(ptr::addr_of!(d.zeros).cast::<__m128i>());
1212
1213
            // Computed against current bcd (rather than the post-bswap bcd) so
1214
            // the mask is derived in parallel with the shuffle on the SSE4.1
1215
            // path.
1216
0
            let mask = _mm_movemask_epi8(_mm_cmpgt_epi8(bcd, _mm_setzero_si128())) as u64;
1217
            // Trailing zeros are in the low bits for SSE4.1, the high bits for
1218
            // SSE2.
1219
0
            let len = if cfg!(target_feature = "sse4.1") {
1220
0
                16 - mask.trailing_zeros()
1221
            } else {
1222
0
                64 - mask.leading_zeros()
1223
            };
1224
1225
            #[cfg(target_feature = "sse4.1")]
1226
            {
1227
                bcd = _mm_shuffle_epi8(
1228
                    bcd,
1229
                    _mm_load_si128(ptr::addr_of!(d.bswap).cast::<__m128i>()),
1230
                ); // SSSE3
1231
            }
1232
1233
0
            DecDigits {
1234
0
                digits: _mm_or_si128(bcd, zeros),
1235
0
                #[cfg(target_feature = "sse4.1")]
1236
0
                unshuffled: (),
1237
0
                num_digits: len as usize,
1238
0
            }
1239
        }
1240
    }
1241
0
}
1242
1243
#[cfg_attr(feature = "no-panic", no_panic)]
1244
#[inline]
1245
0
fn to_digits_32(value: u64, #[allow(unused_variables)] d: &Data) -> DecDigits<f32> {
1246
    #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
1247
    {
1248
        // Inline to_bcd8's SSE4.1 body so we can return the unshuffled xmm too;
1249
        // the exponential-notation path uses it to skip the bswap-via-gpr.
1250
        let abcd_efgh = value + u64::from(NEG10K) * ((value * u64::from(DIV10K_SIG)) >> DIV10K_EXP);
1251
        let bcd_xmm = to_bcd_4x4(_mm_set_epi64x(0, abcd_efgh as i64), d);
1252
        let unshuffled_bcd = unsafe { _mm_cvtsi128_si64(bcd_xmm) } as u64;
1253
        let len = if unshuffled_bcd != 0 {
1254
            8 - unshuffled_bcd.trailing_zeros() / 8
1255
        } else {
1256
            0
1257
        };
1258
        DecDigits {
1259
            digits: unshuffled_bcd.swap_bytes() + ZEROS,
1260
            unshuffled: bcd_xmm,
1261
            num_digits: len as usize,
1262
        }
1263
    }
1264
1265
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
1266
    {
1267
        // Inline to_bcd8's NEON body so we can return the unshuffled vector
1268
        // too; the exponential-notation path uses it to skip the
1269
        // simd->gpr->bswap->simd roundtrip needed to materialize `digits`.
1270
        let abcd_efgh = value + u64::from(NEG10K) * ((value * u64::from(DIV10K_SIG)) >> DIV10K_EXP);
1271
        let unshuffled: uint8x16_t = unsafe {
1272
            let input: int32x4_t =
1273
                vcombine_s32(vreinterpret_s32_u64(vcreate_u64(abcd_efgh)), vdup_n_s32(0));
1274
            to_bcd_4x4(input, d)
1275
        };
1276
        let unshuffled_bcd =
1277
            unsafe { vget_lane_u64(vreinterpret_u64_u8(vget_low_u8(unshuffled)), 0) };
1278
        let len = if unshuffled_bcd != 0 {
1279
            8 - unshuffled_bcd.trailing_zeros() / 8
1280
        } else {
1281
            0
1282
        };
1283
        DecDigits {
1284
            digits: unshuffled_bcd.swap_bytes() + ZEROS,
1285
            unshuffled,
1286
            num_digits: len as usize,
1287
        }
1288
    }
1289
1290
    #[cfg(not(any(
1291
        all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
1292
        all(target_arch = "aarch64", target_feature = "neon", not(miri)),
1293
    )))]
1294
    {
1295
0
        let result = to_bcd8(value);
1296
0
        DecDigits {
1297
0
            digits: result.bcd + ZEROS,
1298
0
            num_digits: result.len,
1299
0
        }
1300
    }
1301
0
}
1302
1303
#[cfg_attr(feature = "no-panic", no_panic)]
1304
0
unsafe fn write_exp_float_simd_32(
1305
0
    buffer: *mut u8,
1306
0
    dig: &DecDigits<f32>,
1307
0
    last_digit: i32,
1308
0
    has_last_digit: bool,
1309
0
    has_extra_digit: bool,
1310
0
    exp_data: u64,
1311
0
    d: &Data,
1312
0
) -> *mut u8 {
1313
    // Packed for insertion into lane 1: byte 0 of `tail` lands at register byte
1314
    // exp_pos (8), so the exp string fills exp_pos..exp_pos+3; the prefix
1315
    // shifts place '0'+last_digit at last_digit_pos (12) and '.' at point_pos
1316
    // (13).
1317
0
    let prefix = (u32::from(b'.') << 8) + u32::from(b'0') + last_digit as u32;
1318
    #[cfg_attr(
1319
        not(any(
1320
            all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)),
1321
            all(target_arch = "aarch64", target_feature = "neon", not(miri)),
1322
        )),
1323
        allow(unused_variables)
1324
    )]
1325
0
    let tail = exp_data | (u64::from(prefix) << 32);
1326
0
    let entry = unsafe {
1327
0
        d.exp_float_shuffles
1328
0
            .get_entry(dig.num_digits as i32, has_last_digit, has_extra_digit)
1329
    };
1330
1331
    #[cfg(all(target_arch = "x86_64", target_feature = "sse4.1", not(miri)))]
1332
    unsafe {
1333
        let ascii: __m128i = _mm_or_si128(
1334
            dig.unshuffled,
1335
            _mm_load_si128(ptr::addr_of!(d.zeros).cast::<__m128i>()),
1336
        );
1337
        let src: __m128i = _mm_insert_epi64(ascii, tail as i64, 1);
1338
        let shuffle: __m128i = _mm_load_si128(entry.shuffle.cast::<__m128i>());
1339
        let out: __m128i = _mm_shuffle_epi8(src, shuffle);
1340
        _mm_storeu_si128(buffer.cast::<__m128i>(), out);
1341
    }
1342
1343
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
1344
    unsafe {
1345
        let ascii: uint8x16_t = vorrq_u8(dig.unshuffled, vdupq_n_u8(b'0'));
1346
        let src: uint8x16_t =
1347
            vreinterpretq_u8_u64(vsetq_lane_u64(tail, vreinterpretq_u64_u8(ascii), 1));
1348
        let shuffle: uint8x16_t = vld1q_u8(entry.shuffle);
1349
        let out: uint8x16_t = vqtbl1q_u8(src, shuffle);
1350
        vst1q_u8(buffer, out);
1351
    }
1352
1353
0
    let length = entry.length as usize - usize::from((exp_data & 0xff000000) == 0);
1354
0
    unsafe { buffer.add(length) }
1355
0
}
1356
1357
struct ToDecimalResult {
1358
    sig: i64,
1359
    exp: i32,
1360
    last_digit: u8,
1361
    has_last_digit: bool,
1362
}
1363
1364
// Here be 🐉s.
1365
// Converts a binary FP number bin_sig * 2**bin_exp to the shortest decimal
1366
// representation, where bin_exp = raw_exp - exp_offset.
1367
#[cfg_attr(feature = "no-panic", no_panic)]
1368
#[inline]
1369
0
fn to_decimal<Float, UInt>(bin_sig: UInt, raw_exp: i64, regular: bool, d: &Data) -> ToDecimalResult
1370
0
where
1371
0
    Float: FloatTraits,
1372
0
    UInt: traits::UInt,
1373
{
1374
0
    let bin_exp = raw_exp - i64::from(Float::EXP_OFFSET);
1375
0
    let num_bits = mem::size_of::<UInt>() as i32 * 8;
1376
    const EXTRA_SHIFT: usize = ExpShiftTable::EXTRA_SHIFT;
1377
1378
0
    if !regular {
1379
0
        let dec_exp = compute_dec_exp(bin_exp as i32, false);
1380
0
        let shift = compute_exp_shift(bin_exp as i32, dec_exp + 1).wrapping_add(EXTRA_SHIFT as u8);
1381
0
        let pow10 = unsafe { d.pow10_significands.get_unchecked(-dec_exp - 1) };
1382
0
        let p = umul192_hi128(pow10.hi, pow10.lo, (bin_sig << shift).into());
1383
1384
0
        let mut integral = p.hi >> EXTRA_SHIFT;
1385
0
        let fractional = (p.hi << (64 - EXTRA_SHIFT)) | (p.lo >> EXTRA_SHIFT);
1386
1387
0
        let half_ulp = pow10.hi >> (EXTRA_SHIFT + 1 - shift as usize);
1388
0
        let round_up = half_ulp > u64::MAX - fractional;
1389
0
        let round_down = (half_ulp >> 1) > fractional;
1390
0
        integral += u64::from(round_up);
1391
1392
0
        let mut digit = umul128_add_hi64(fractional, 10, (1 << 63) - 1) as i32;
1393
0
        let lo = umul128_add_hi64(fractional.wrapping_sub(half_ulp >> 1), 10, !0) as i32;
1394
0
        if digit < lo {
1395
0
            digit = lo;
1396
0
        }
1397
        return ToDecimalResult {
1398
0
            sig: integral as i64,
1399
0
            exp: dec_exp,
1400
0
            last_digit: digit as u8,
1401
0
            has_last_digit: !(round_up || round_down),
1402
        };
1403
0
    }
1404
1405
    const LOG10_2_SIG: u64 = 78_913;
1406
    const LOG10_2_EXP: i32 = 18;
1407
    #[allow(unused_mut)]
1408
0
    let mut dec_exp = if USE_UMUL128_HI64 {
1409
0
        umul128_hi64(bin_exp as u64, LOG10_2_SIG << (64 - LOG10_2_EXP)) as i32
1410
    } else {
1411
0
        compute_dec_exp(bin_exp as i32, true)
1412
    };
1413
    #[cfg(not(miri))]
1414
    #[allow(unused_unsafe)]
1415
0
    unsafe {
1416
0
        // Force 32-bit reg for sxtw addressing.
1417
0
        #[cfg(target_arch = "x86_64")]
1418
0
        asm!("/*{0:e}*/", inout(reg) dec_exp);
1419
0
        #[cfg(target_arch = "aarch64")]
1420
0
        asm!("/*{0:w}*/", inout(reg) dec_exp);
1421
0
    }
1422
0
    let mut shift = if ExpShiftTable::ENABLE {
1423
0
        *unsafe {
1424
0
            d.exp_shifts
1425
0
                .data
1426
0
                .get_unchecked((bin_exp + i64::from(f64::EXP_OFFSET)) as usize)
1427
0
        }
1428
    } else {
1429
0
        compute_exp_shift(bin_exp as i32, dec_exp + 1).wrapping_add(EXTRA_SHIFT as u8)
1430
    };
1431
0
    let even = UInt::from(1) - (bin_sig & UInt::from(1));
1432
1433
0
    if num_bits == 32 {
1434
        const EXTRA_SHIFT: usize = 34;
1435
0
        shift += (EXTRA_SHIFT - ExpShiftTable::EXTRA_SHIFT) as u8;
1436
0
        let pow10_hi = unsafe { d.pow10_significands.get_unchecked(-dec_exp - 1) }.hi;
1437
0
        let p = umul128_hi64(pow10_hi + 1, bin_sig.into() << shift);
1438
1439
0
        let mut integral = p >> EXTRA_SHIFT;
1440
0
        let fractional = p & ((1u64 << EXTRA_SHIFT) - 1);
1441
1442
0
        let half_ulp = (pow10_hi >> (65 - shift as usize)) + even.into();
1443
0
        let round_up = ((fractional + half_ulp) >> EXTRA_SHIFT) != 0;
1444
0
        let round_down = half_ulp > fractional;
1445
0
        integral += u64::from(round_up);
1446
1447
0
        let mut digit = ((fractional * 10 + (1u64 << (EXTRA_SHIFT - 1))) >> EXTRA_SHIFT) as i32;
1448
0
        if fractional == (1u64 << (EXTRA_SHIFT - 2)) {
1449
0
            digit = 2; // Round 2.5 to 2.
1450
0
        }
1451
        return ToDecimalResult {
1452
0
            sig: integral as i64,
1453
0
            exp: dec_exp,
1454
0
            last_digit: digit as u8,
1455
0
            has_last_digit: !(round_up || round_down),
1456
        };
1457
0
    }
1458
1459
    // An optimization by Xiang JunBo:
1460
    // Scale by 10**(-dec_exp-1) to directly produce the shorter candidate
1461
    // (15-16 digits), deriving the extra digit from the fractional part.
1462
    // This eliminates div10 from the critical path.
1463
    //
1464
    // value = 5.0507837461e-27
1465
    // next  = 5.0507837461000010e-27
1466
    //
1467
    // c = integral.fractional' = 5050783746100000.3153987... (value)
1468
    //                            5050783746100001.0328635... (next)
1469
    //                 half_ulp =                0.3587324...
1470
    //
1471
    // fractional = fractional' * 2**64 = 5818079786399166407
1472
    //
1473
    //    5050783746100000.0       c               upper    5050783746100001.0
1474
    //             s              l|   L             |               S
1475
    // ──┬────┬────┼────┬────┬────┼*───┼────┬────┬───*┬────┬────┬────┼─*──┬───
1476
    //  .8   .9   .0   .1   .2   .3   .4   .5   .6   .7   .8   .9   .0 | .1
1477
    //           └─────────────────┼─────────────────┘                next
1478
    //                            1ulp
1479
    //
1480
    // s - shorter underestimate, S - shorter overestimate
1481
    // l - longer underestimate,  L - longer overestimate
1482
0
    let pow10 = unsafe { d.pow10_significands.get_unchecked(-dec_exp - 1) };
1483
0
    let p = umul192_hi128(pow10.hi, pow10.lo, (bin_sig << shift).into());
1484
1485
0
    let mut integral = p.hi >> EXTRA_SHIFT;
1486
0
    let fractional = (p.hi << (64 - EXTRA_SHIFT)) | (p.lo >> EXTRA_SHIFT);
1487
1488
0
    let half_ulp = (pow10.hi >> (EXTRA_SHIFT + 1 - shift as usize)) + even.into();
1489
0
    let round_up = fractional.wrapping_add(half_ulp) < fractional;
1490
0
    let round_down = half_ulp > fractional;
1491
0
    integral += u64::from(round_up); // Compute integral before digit.
1492
1493
    // Derive the extra digit from the fractional part (parallel with rounding).
1494
0
    let mut digit = umul128_add_hi64(fractional, 10, d.biased_half.get()) as i32;
1495
0
    if fractional == (1u64 << 62) {
1496
0
        digit = 2; // Round 2.5 to 2.
1497
0
    }
1498
    ToDecimalResult {
1499
0
        sig: integral as i64,
1500
0
        exp: dec_exp,
1501
0
        last_digit: digit as u8,
1502
0
        has_last_digit: !(round_up || round_down),
1503
    }
1504
0
}
Unexecuted instantiation: zmij::to_decimal::<f64, u64>
Unexecuted instantiation: zmij::to_decimal::<f32, u32>
1505
1506
/// Writes the shortest correctly rounded decimal representation of `value` to
1507
/// `buffer`. `buffer` should point to a buffer of size `buffer_size` or larger.
1508
#[cfg_attr(feature = "no-panic", no_panic)]
1509
0
unsafe fn write<Float>(value: Float, mut buffer: *mut u8) -> *mut u8
1510
0
where
1511
0
    Float: FloatTraits,
1512
{
1513
0
    let bits = value.to_bits();
1514
    // It is beneficial to extract exponent and significand early.
1515
0
    let bin_exp = Float::get_exp(bits); // binary exponent
1516
0
    let bin_sig = Float::get_sig(bits); // binary significand
1517
1518
0
    unsafe {
1519
0
        *buffer = b'-';
1520
0
    }
1521
0
    buffer = unsafe { buffer.add(usize::from(Float::is_negative(bits))) };
1522
1523
    #[allow(unused_mut)]
1524
0
    let mut d = ptr::addr_of!(STATIC_DATA);
1525
0
    let d = unsafe {
1526
        // Load constants from memory.
1527
        #[cfg(all(any(target_arch = "aarch64", target_arch = "x86_64"), not(miri)))]
1528
0
        asm!("/*{0}*/", inout(reg) d);
1529
0
        &*d
1530
    };
1531
0
    let threshold = if Float::NUM_BITS == 64 {
1532
0
        d.threshold.get()
1533
    } else {
1534
0
        10_000_000
1535
    };
1536
1537
    let mut dec;
1538
0
    if bin_exp == 0 {
1539
0
        if bin_sig == Float::SigType::from(0) {
1540
            return unsafe {
1541
0
                *buffer = b'0';
1542
0
                *buffer.add(1) = b'.';
1543
0
                *buffer.add(2) = b'0';
1544
0
                buffer.add(3)
1545
            };
1546
0
        }
1547
0
        dec = to_decimal::<Float, Float::SigType>(bin_sig, 1, true, d);
1548
0
        let mut dec_sig =
1549
0
            dec.sig * 10 + (-i64::from(dec.has_last_digit) & i64::from(dec.last_digit));
1550
0
        let mut dec_exp = dec.exp;
1551
0
        while dec_sig < threshold as i64 {
1552
0
            dec_sig *= 10;
1553
0
            dec_exp -= 1;
1554
0
        }
1555
0
        let d = div10(dec_sig as u64);
1556
0
        let last_digit = dec_sig - d as i64 * 10;
1557
0
        dec = ToDecimalResult {
1558
0
            sig: d as i64,
1559
0
            exp: dec_exp,
1560
0
            last_digit: last_digit as u8,
1561
0
            has_last_digit: last_digit != 0,
1562
0
        };
1563
0
    } else {
1564
0
        dec = to_decimal::<Float, Float::SigType>(
1565
0
            bin_sig | Float::IMPLICIT_BIT,
1566
0
            bin_exp,
1567
0
            bin_sig != Float::SigType::from(0),
1568
0
            d,
1569
0
        );
1570
0
    }
1571
0
    let mut has_last_digit = dec.has_last_digit;
1572
0
    let has_extra_digit = dec.sig >= threshold as i64;
1573
0
    let mut dec_exp = dec.exp + Float::MAX_DIGITS10 as i32 - 2 + i32::from(has_extra_digit);
1574
0
    if Float::NUM_BITS == 32 && dec.sig < 1_000_000 {
1575
0
        dec.sig = 10 * dec.sig + (-i64::from(has_last_digit) & i64::from(dec.last_digit));
1576
0
        has_last_digit = false;
1577
0
        dec_exp -= 1;
1578
0
    }
1579
1580
    // Write significand.
1581
0
    let dig = Float::to_digits(dec.sig as u64, d);
1582
1583
0
    if Float::NUM_BITS == 32
1584
0
        && ExpFloatShuffleTable::ENABLE
1585
0
        && !Float::FIXED_DEC_EXP.contains(&dec_exp)
1586
    {
1587
        unsafe {
1588
0
            let exp_data = *d
1589
0
                .exp_strings
1590
0
                .data
1591
0
                .get_unchecked((dec_exp + ExpStringTable::OFFSET) as usize);
1592
0
            return Float::write_exp_float_simd(
1593
0
                buffer,
1594
0
                &dig,
1595
0
                i32::from(dec.last_digit),
1596
0
                has_last_digit,
1597
0
                has_extra_digit,
1598
0
                exp_data,
1599
0
                d,
1600
            );
1601
        }
1602
0
    }
1603
1604
0
    let bcd_size = if Float::NUM_BITS == 64 { 16 } else { 8 };
1605
0
    unsafe {
1606
0
        buffer
1607
0
            .add(usize::from(has_extra_digit))
1608
0
            .cast::<Float::DecDigitsType>()
1609
0
            .write_unaligned(dig.digits);
1610
0
        buffer
1611
0
            .add(usize::from(has_extra_digit) + bcd_size)
1612
0
            .write(b'0' + dec.last_digit);
1613
0
    }
1614
0
    let length = usize::from(has_extra_digit)
1615
0
        + if has_last_digit {
1616
0
            bcd_size + 1
1617
        } else {
1618
0
            dig.num_digits
1619
        }
1620
        - 1;
1621
1622
0
    if Float::FIXED_DEC_EXP.contains(&dec_exp) {
1623
0
        if length as i32 - 1 <= dec_exp {
1624
            // 1234e7 -> 12340000000.0
1625
            return unsafe {
1626
0
                ptr::copy(buffer.add(1), buffer, length);
1627
0
                ptr::write_bytes(buffer.add(length), b'0', dec_exp as usize + 3 - length);
1628
0
                *buffer.add(dec_exp as usize + 1) = b'.';
1629
0
                buffer.add(dec_exp as usize + 3)
1630
            };
1631
0
        } else if 0 <= dec_exp {
1632
            // 1234e-2 -> 12.34
1633
            return unsafe {
1634
0
                ptr::copy(buffer.add(1), buffer, dec_exp as usize + 1);
1635
0
                *buffer.add(dec_exp as usize + 1) = b'.';
1636
0
                buffer.add(length + 1)
1637
            };
1638
        } else {
1639
            // 1234e-6 -> 0.001234
1640
            return unsafe {
1641
0
                ptr::copy(buffer.add(1), buffer.add((1 - dec_exp) as usize), length);
1642
0
                ptr::write_bytes(buffer, b'0', (1 - dec_exp) as usize);
1643
0
                *buffer.add(1) = b'.';
1644
0
                buffer.add((1 - dec_exp) as usize + length)
1645
            };
1646
        }
1647
0
    }
1648
1649
0
    unsafe {
1650
0
        // 1234e30 -> 1.234e33
1651
0
        *buffer = *buffer.add(1);
1652
0
        *buffer.add(1) = b'.';
1653
0
    }
1654
0
    buffer = unsafe { buffer.add(length + usize::from(length > 1)) };
1655
1656
    // Write exponent.
1657
0
    if ExpStringTable::ENABLE {
1658
0
        let mut exp_data = unsafe {
1659
0
            *d.exp_strings
1660
0
                .data
1661
0
                .get_unchecked((dec_exp + ExpStringTable::OFFSET) as usize)
1662
        };
1663
0
        let len = (exp_data >> 48) as usize;
1664
0
        exp_data = exp_data.to_le();
1665
        unsafe {
1666
0
            ptr::copy_nonoverlapping(
1667
0
                ptr::addr_of!(exp_data).cast::<u8>(),
1668
0
                buffer,
1669
0
                if Float::MAX_10_EXP >= 100 { 5 } else { 4 },
1670
            );
1671
0
            return buffer.add(len);
1672
        }
1673
0
    }
1674
0
    let sign_ptr = buffer;
1675
0
    let e_sign = if dec_exp >= 0 {
1676
0
        (u16::from(b'+') << 8) | u16::from(b'e')
1677
    } else {
1678
0
        (u16::from(b'-') << 8) | u16::from(b'e')
1679
    };
1680
0
    buffer = unsafe { buffer.add(1) };
1681
0
    dec_exp = if dec_exp >= 0 { dec_exp } else { -dec_exp };
1682
0
    buffer = unsafe { buffer.add(usize::from(dec_exp >= 10)) };
1683
0
    if Float::MAX_10_EXP >= 100 {
1684
        // digit = dec_exp / 100
1685
0
        let digit = if USE_UMUL128_HI64 {
1686
0
            umul128_hi64(dec_exp as u64, 0x290000000000000) as u32
1687
        } else {
1688
0
            (dec_exp as u32 * DIV100_SIG) >> DIV100_EXP
1689
        };
1690
0
        unsafe {
1691
0
            *buffer = b'0' + digit as u8;
1692
0
        }
1693
0
        buffer = unsafe { buffer.add(usize::from(dec_exp >= 100)) };
1694
0
        dec_exp -= (digit * 100) as i32;
1695
0
    }
1696
    unsafe {
1697
0
        buffer
1698
0
            .cast::<u16>()
1699
0
            .write_unaligned(*digits2(dec_exp as usize));
1700
0
        sign_ptr.cast::<u16>().write_unaligned(e_sign.to_le());
1701
0
        buffer.add(2)
1702
    }
1703
0
}
Unexecuted instantiation: zmij::write::<f64>
Unexecuted instantiation: zmij::write::<f32>
1704
1705
/// Safe API for formatting floating point numbers to text.
1706
///
1707
/// ## Example
1708
///
1709
/// ```
1710
/// let mut buffer = zmij::Buffer::new();
1711
/// let printed = buffer.format_finite(1.234);
1712
/// assert_eq!(printed, "1.234");
1713
/// ```
1714
pub struct Buffer {
1715
    bytes: [MaybeUninit<u8>; BUFFER_SIZE],
1716
}
1717
1718
impl Buffer {
1719
    /// This is a cheap operation; you don't need to worry about reusing buffers
1720
    /// for efficiency.
1721
    #[inline]
1722
    #[cfg_attr(feature = "no-panic", no_panic)]
1723
0
    pub fn new() -> Self {
1724
0
        let bytes = [MaybeUninit::<u8>::uninit(); BUFFER_SIZE];
1725
0
        Buffer { bytes }
1726
0
    }
Unexecuted instantiation: <zmij::Buffer>::new
Unexecuted instantiation: <zmij::Buffer>::new
1727
1728
    /// Print a floating point number into this buffer and return a reference to
1729
    /// its string representation within the buffer.
1730
    ///
1731
    /// # Special cases
1732
    ///
1733
    /// This function formats NaN as the string "NaN", positive infinity as
1734
    /// "inf", and negative infinity as "-inf" to match std::fmt.
1735
    ///
1736
    /// If your input is known to be finite, you may get better performance by
1737
    /// calling the `format_finite` method instead of `format` to avoid the
1738
    /// checks for special cases.
1739
    #[cfg_attr(feature = "no-panic", no_panic)]
1740
0
    pub fn format<F: Float>(&mut self, f: F) -> &str {
1741
0
        if f.is_nonfinite() {
1742
0
            f.format_nonfinite()
1743
        } else {
1744
0
            self.format_finite(f)
1745
        }
1746
0
    }
Unexecuted instantiation: <zmij::Buffer>::format::<f64>
Unexecuted instantiation: <zmij::Buffer>::format::<_>
1747
1748
    /// Print a floating point number into this buffer and return a reference to
1749
    /// its string representation within the buffer.
1750
    ///
1751
    /// # Special cases
1752
    ///
1753
    /// This function **does not** check for NaN or infinity. If the input
1754
    /// number is not a finite float, the printed representation will be some
1755
    /// correctly formatted but unspecified numerical value.
1756
    ///
1757
    /// Please check [`is_finite`] yourself before calling this function, or
1758
    /// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
1759
    ///
1760
    /// [`is_finite`]: f64::is_finite
1761
    /// [`is_nan`]: f64::is_nan
1762
    /// [`is_infinite`]: f64::is_infinite
1763
    #[cfg_attr(feature = "no-panic", no_panic)]
1764
0
    pub fn format_finite<F: Float>(&mut self, f: F) -> &str {
1765
        unsafe {
1766
0
            let end = f.write_to_zmij_buffer(self.bytes.as_mut_ptr().cast::<u8>());
1767
0
            let len = end.offset_from(self.bytes.as_ptr().cast::<u8>()) as usize;
1768
0
            let slice = slice::from_raw_parts(self.bytes.as_ptr().cast::<u8>(), len);
1769
0
            str::from_utf8_unchecked(slice)
1770
        }
1771
0
    }
Unexecuted instantiation: <zmij::Buffer>::format_finite::<f64>
Unexecuted instantiation: <zmij::Buffer>::format_finite::<f32>
Unexecuted instantiation: <zmij::Buffer>::format_finite::<_>
1772
}
1773
1774
/// A floating point number, f32 or f64, that can be written into a
1775
/// [`zmij::Buffer`][Buffer].
1776
///
1777
/// This trait is sealed and cannot be implemented for types outside of the
1778
/// `zmij` crate.
1779
#[allow(unknown_lints)] // rustc older than 1.74
1780
#[allow(private_bounds)]
1781
pub trait Float: private::Sealed {}
1782
impl Float for f32 {}
1783
impl Float for f64 {}
1784
1785
mod private {
1786
    pub trait Sealed: crate::traits::Float {
1787
        fn is_nonfinite(self) -> bool;
1788
        fn format_nonfinite(self) -> &'static str;
1789
        unsafe fn write_to_zmij_buffer(self, buffer: *mut u8) -> *mut u8;
1790
    }
1791
1792
    impl Sealed for f32 {
1793
        #[inline]
1794
0
        fn is_nonfinite(self) -> bool {
1795
            const EXP_MASK: u32 = 0x7f800000;
1796
0
            let bits = self.to_bits();
1797
0
            bits & EXP_MASK == EXP_MASK
1798
0
        }
1799
1800
        #[cold]
1801
        #[cfg_attr(feature = "no-panic", inline)]
1802
0
        fn format_nonfinite(self) -> &'static str {
1803
            const MANTISSA_MASK: u32 = 0x007fffff;
1804
            const SIGN_MASK: u32 = 0x80000000;
1805
0
            let bits = self.to_bits();
1806
0
            if bits & MANTISSA_MASK != 0 {
1807
0
                crate::NAN
1808
0
            } else if bits & SIGN_MASK != 0 {
1809
0
                crate::NEG_INFINITY
1810
            } else {
1811
0
                crate::INFINITY
1812
            }
1813
0
        }
1814
1815
        #[cfg_attr(feature = "no-panic", inline)]
1816
0
        unsafe fn write_to_zmij_buffer(self, buffer: *mut u8) -> *mut u8 {
1817
0
            unsafe { crate::write(self, buffer) }
1818
0
        }
1819
    }
1820
1821
    impl Sealed for f64 {
1822
        #[inline]
1823
0
        fn is_nonfinite(self) -> bool {
1824
            const EXP_MASK: u64 = 0x7ff0000000000000;
1825
0
            let bits = self.to_bits();
1826
0
            bits & EXP_MASK == EXP_MASK
1827
0
        }
Unexecuted instantiation: <f64 as zmij::private::Sealed>::is_nonfinite
Unexecuted instantiation: <f64 as zmij::private::Sealed>::is_nonfinite
1828
1829
        #[cold]
1830
        #[cfg_attr(feature = "no-panic", inline)]
1831
0
        fn format_nonfinite(self) -> &'static str {
1832
            const MANTISSA_MASK: u64 = 0x000fffffffffffff;
1833
            const SIGN_MASK: u64 = 0x8000000000000000;
1834
0
            let bits = self.to_bits();
1835
0
            if bits & MANTISSA_MASK != 0 {
1836
0
                crate::NAN
1837
0
            } else if bits & SIGN_MASK != 0 {
1838
0
                crate::NEG_INFINITY
1839
            } else {
1840
0
                crate::INFINITY
1841
            }
1842
0
        }
1843
1844
        #[cfg_attr(feature = "no-panic", inline)]
1845
0
        unsafe fn write_to_zmij_buffer(self, buffer: *mut u8) -> *mut u8 {
1846
0
            unsafe { crate::write(self, buffer) }
1847
0
        }
1848
    }
1849
}
1850
1851
impl Default for Buffer {
1852
    #[inline]
1853
    #[cfg_attr(feature = "no-panic", no_panic)]
1854
0
    fn default() -> Self {
1855
0
        Buffer::new()
1856
0
    }
1857
}