Skip to main content

zmij/
lib.rs

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)))]
74mod stdarch_x86;
75#[cfg(test)]
76mod tests;
77mod traits;
78
79#[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
80use 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))]
91use 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))]
100use crate::stdarch_x86::{
101    _mm_shuffle_epi32, _mm_slli_epi16, _mm_slli_epi32, _mm_srli_epi16, _mm_sub_epi16, _MM_SHUFFLE,
102};
103use crate::traits::Float as _;
104#[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
105use 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)))]
116use core::arch::asm;
117use core::mem::{self, MaybeUninit};
118use core::ops::RangeInclusive;
119use core::ptr;
120use core::slice;
121use core::str;
122#[cfg(feature = "no-panic")]
123use no_panic::no_panic;
124
125const BUFFER_SIZE: usize = 24;
126const NAN: &str = "NaN";
127const INFINITY: &str = "inf";
128const 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.
132struct AArch64Mem<const VALUE: u64> {
133    #[cfg(target_arch = "aarch64")]
134    value: u64,
135}
136
137impl<const VALUE: u64> AArch64Mem<VALUE> {
138    const fn new() -> Self {
139        AArch64Mem {
140            #[cfg(target_arch = "aarch64")]
141            value: VALUE,
142        }
143    }
144
145    #[cfg_attr(not(target_arch = "aarch64"), allow(clippy::unused_self))]
146    const fn get(&self) -> u64 {
147        #[cfg(target_arch = "aarch64")]
148        {
149            self.value
150        }
151
152        #[cfg(not(target_arch = "aarch64"))]
153        {
154            VALUE
155        }
156    }
157}
158
159#[derive(#[automatically_derived]
impl ::core::marker::Copy for uint128 { }Copy, #[automatically_derived]
impl ::core::clone::Clone for uint128 {
    #[inline]
    fn clone(&self) -> uint128 {
        let _: ::core::clone::AssertParamIsClone<u64>;
        *self
    }
}Clone)]
160#[cfg_attr(test, derive(Debug, PartialEq))]
161struct uint128 {
162    hi: u64,
163    lo: u64,
164}
165
166// Use umul128_hi64 for division.
167const USE_UMUL128_HI64: bool = falsecfg!(target_vendor = "apple");
168
169// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
170const fn umul128(x: u64, y: u64) -> u128 {
171    x as u128 * y as u128
172}
173
174#[inline]
175const fn umul128_hi64(x: u64, y: u64) -> u64 {
176    (umul128(x, y) >> 64) as u64
177}
178
179// Returns (x * y + c) >> 64.
180#[cfg_attr(feature = "no-panic", no_panic)]
181fn umul128_add_hi64(x: u64, y: u64, c: u64) -> u64 {
182    ((u128::from(x) * u128::from(y) + u128::from(c)) >> 64) as u64
183}
184
185#[cfg_attr(feature = "no-panic", no_panic)]
186fn umul192_hi128(x_hi: u64, x_lo: u64, y: u64) -> uint128 {
187    let p = umul128(x_hi, y);
188    let lo = (p as u64).wrapping_add((umul128(x_lo, y) >> 64) as u64);
189    uint128 {
190        hi: (p >> 64) as u64 + u64::from(lo < p as u64),
191        lo,
192    }
193}
194
195// Returns x / 10 for x <= 2**62.
196#[cfg_attr(feature = "no-panic", no_panic)]
197fn div10(x: u64) -> u64 {
198    if true {
    if !(x < (1 << 62)) {
        ::core::panicking::panic("assertion failed: x < (1 << 62)")
    };
};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    umul128_hi64(x, DIV10_SIG64)
202}
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.
206const fn compute_dec_exp(bin_exp: i32, regular: bool) -> i32 {
207    if true {
    if !(bin_exp >= -1334 && bin_exp <= 2620) {
        ::core::panicking::panic("assertion failed: bin_exp >= -1334 && bin_exp <= 2620")
    };
};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    (bin_exp * LOG10_2_SIG - !regular as i32 * LOG10_3_OVER_4_SIG) >> LOG10_2_EXP
214}
215
216trait 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    fn is_negative(bits: Self::SigType) -> bool {
241        (bits >> (Self::NUM_BITS - 1)) != Self::SigType::from(0)
242    }
243
244    fn get_sig(bits: Self::SigType) -> Self::SigType {
245        bits & (Self::IMPLICIT_BIT - Self::SigType::from(1))
246    }
247
248    fn get_exp(bits: Self::SigType) -> i64 {
249        (bits << 1u8 >> (Self::NUM_SIG_BITS + 1)).into() as i64
250    }
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
268impl 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    fn to_bits(self) -> Self::SigType {
286        self.to_bits()
287    }
288
289    #[inline]
290    fn to_digits(value: u64, d: &Data) -> DecDigits<Self> {
291        to_digits_32(value, d)
292    }
293
294    #[inline]
295    unsafe fn write_exp_float_simd(
296        buffer: *mut u8,
297        dig: &DecDigits<Self>,
298        last_digit: i32,
299        has_last_digit: bool,
300        has_extra_digit: bool,
301        exp_data: u64,
302        d: &Data,
303    ) -> *mut u8 {
304        unsafe {
305            write_exp_float_simd_32(
306                buffer,
307                dig,
308                last_digit,
309                has_last_digit,
310                has_extra_digit,
311                exp_data,
312                d,
313            )
314        }
315    }
316}
317
318impl 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    fn to_bits(self) -> Self::SigType {
345        self.to_bits()
346    }
347
348    #[inline]
349    fn to_digits(value: u64, d: &Data) -> DecDigits<Self> {
350        to_digits_64(value, d)
351    }
352
353    #[inline]
354    unsafe fn write_exp_float_simd(
355        _buffer: *mut u8,
356        _dig: &DecDigits<Self>,
357        _last_digit: i32,
358        _has_last_digit: bool,
359        _has_extra_digit: bool,
360        _exp_data: u64,
361        _d: &Data,
362    ) -> *mut u8 {
363        ptr::null_mut()
364    }
365}
366
367#[rustfmt::skip]
368const 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]
382const 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]
409const 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))]
418struct Pow10SignificandTable {
419    data: [u64; if Self::COMPRESS {
420        0
421    } else {
422        Self::NUM_POW10S * 2
423    }],
424}
425
426impl Pow10SignificandTable {
427    const COMPRESS: bool = falsecfg!(opt_level = "s");
428    const SPLIT_TABLES: bool = !Self::COMPRESS && falsecfg!(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    const fn compute(i: u32) -> uint128 {
434        const STRIDE: u32 = POW10_MINOR.len() as u32;
435        let m = unsafe { *POW10_MINOR.as_ptr().add(((i + 10) % STRIDE) as usize) };
436        let h = unsafe { *POW10_MAJOR.as_ptr().add(((i + 10) / STRIDE) as usize) };
437
438        let h1 = umul128_hi64(h.lo, m);
439
440        let c0 = h.lo.wrapping_mul(m);
441        let c1 = h1.wrapping_add(h.hi.wrapping_mul(m));
442        let c2 = (c1 < h1) as u64 + umul128_hi64(h.hi, m);
443
444        let mut result = if (c2 >> 63) != 0 {
445            uint128 { hi: c2, lo: c1 }
446        } else {
447            uint128 {
448                hi: (c2 << 1) | (c1 >> 63),
449                lo: (c1 << 1) | (c0 >> 63),
450            }
451        };
452        result.lo -=
453            ((unsafe { *POW10_FIXUPS.as_ptr().add((i >> 5) as usize) } >> (i & 31)) & 1) as u64;
454        result
455    }
456
457    const fn new() -> Self {
458        let mut data = [0; if Self::COMPRESS {
459            0
460        } else {
461            Self::NUM_POW10S * 2
462        }];
463
464        let mut i = 0;
465        while i < Self::NUM_POW10S && !Self::COMPRESS {
466            let result = Self::compute(i as u32);
467            if Self::SPLIT_TABLES {
468                data[Self::NUM_POW10S - i - 1] = result.hi;
469                data[Self::NUM_POW10S * 2 - i - 1] = result.lo;
470            } else {
471                data[i * 2] = result.hi;
472                data[i * 2 + 1] = result.lo;
473            }
474            i += 1;
475        }
476
477        Pow10SignificandTable { data }
478    }
479
480    #[inline]
481    unsafe fn get_unchecked(&self, dec_exp: i32) -> uint128 {
482        const DEC_EXP_MIN: i32 = -293;
483        let i = dec_exp - DEC_EXP_MIN;
484        if Self::COMPRESS {
485            return Self::compute(i as u32);
486        }
487        if !Self::SPLIT_TABLES {
488            let p = unsafe { self.data.as_ptr().add((i * 2) as usize) };
489            return uint128 {
490                hi: unsafe { *p },
491                lo: unsafe { *p.add(1) },
492            };
493        }
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            let mut p = self
504                .data
505                .as_ptr()
506                .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            asm!("/*{0}*/", inout(reg) p);
509            uint128 {
510                hi: *p.offset(!(dec_exp as isize)),
511                lo: *p.offset(!(dec_exp as isize) + Self::NUM_POW10S as isize),
512            }
513        }
514    }
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]
535const fn compute_exp_shift(bin_exp: i32, dec_exp: i32) -> u8 {
536    if true {
    if !(dec_exp >= -350 && dec_exp <= 350) {
        ::core::panicking::panic("assertion failed: dec_exp >= -350 && dec_exp <= 350")
    };
};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    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    (bin_exp + pow10_bin_exp + 1) as u8
544}
545
546struct ExpShiftTable {
547    data: [u8; if Self::ENABLE {
548        f64::EXP_MASK as usize + 1
549    } else {
550        0
551    }],
552}
553
554impl ExpShiftTable {
555    const ENABLE: bool = truecfg!(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    const fn new() -> Self {
561        let mut data = [0u8; if Self::ENABLE {
562            f64::EXP_MASK as usize + 1
563        } else {
564            0
565        }];
566
567        let mut raw_exp = 0;
568        while raw_exp < data.len() && Self::ENABLE {
569            let mut bin_exp = raw_exp as i32 - f64::EXP_OFFSET;
570            if raw_exp == 0 {
571                bin_exp += 1;
572            }
573            let dec_exp = compute_dec_exp(bin_exp, true);
574            data[raw_exp] =
575                compute_exp_shift(bin_exp, dec_exp + 1).wrapping_add(Self::EXTRA_SHIFT as u8);
576            raw_exp += 1;
577        }
578
579        ExpShiftTable { data }
580    }
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.
585struct 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
593impl ExpStringTable {
594    const ENABLE: bool = truecfg!(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    const fn new() -> Self {
599        let mut data = [0u64; if Self::ENABLE {
600            (f64::MAX_10_EXP - Self::MIN_DEC_EXP + 1) as usize
601        } else {
602            0
603        }];
604
605        let mut e = Self::MIN_DEC_EXP;
606        while e <= f64::MAX_10_EXP && Self::ENABLE {
607            let abs_e = e.unsigned_abs() as u64;
608            let mut val = abs_e % 10 + b'0' as u64;
609            if abs_e >= 10 {
610                val = (val << 8) | (abs_e / 10 % 10 + b'0' as u64);
611            }
612            if abs_e >= 100 {
613                val = (val << 8) | (abs_e / 100 + b'0' as u64);
614            }
615            let len = 3 + (abs_e >= 10) as u64 + (abs_e >= 100) as u64;
616            data[(e + Self::OFFSET) as usize] = (len << 48)
617                | (val << 16)
618                | (if e >= 0 { b'+' as u64 } else { b'-' as u64 } << 8)
619                | b'e' as u64;
620            e += 1;
621        }
622
623        ExpStringTable { data }
624    }
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))]
638struct ExpFloatShuffleTable {
639    data: [u8; if Self::ENABLE { 32 * 16 } else { 0 }],
640}
641
642struct 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
654impl ExpFloatShuffleTable {
655    const ENABLE: bool = falsecfg!(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    unsafe fn get_entry(
665        &self,
666        num_digits: i32,
667        has_last_digit: bool,
668        has_extra_digit: bool,
669    ) -> ExpFloatShuffleTableEntry {
670        let idx = (num_digits - 1) * 4 + i32::from(has_last_digit) * 2 + i32::from(has_extra_digit);
671        ExpFloatShuffleTableEntry {
672            shuffle: unsafe { self.data.as_ptr().add(idx as usize * 16) },
673            length: *unsafe { self.data.get_unchecked(idx as usize * 16 + 15) },
674        }
675    }
676
677    const fn new() -> Self {
678        let mut data = [0u8; if Self::ENABLE { 32 * 16 } else { 0 }];
679
680        let mut idx = 0;
681        while idx < 32 && Self::ENABLE {
682            let num_digits = (idx >> 2) + 1;
683            let has_last_digit = ((idx >> 1) & 1) != 0;
684            let has_extra_digit = (idx & 1) != 0;
685
686            let out = idx * 16;
687            let mut i = 0;
688            while i < 16 {
689                data[out + i] = 0x80; // shuffle high bit: output 0
690                i += 1;
691            }
692            let leading_digit_pos = if has_extra_digit { 7 } else { 6 };
693            let mut length = 0;
694            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                data[out + length] = leading_digit_pos;
699                length += 1;
700                data[out + length] = Self::POINT_POS;
701                length += 1;
702                let mut i = leading_digit_pos - 1;
703                loop {
704                    data[out + length] = i;
705                    length += 1;
706                    if i == 0 {
707                        break;
708                    }
709                    i -= 1;
710                }
711                data[out + length] = Self::LAST_DIGIT_POS;
712                length += 1;
713            } else {
714                length = num_digits + has_extra_digit as usize;
715                // Drop the '.' for single-digit output: "5e+02", not "5.0e+02".
716                if length == 2 {
717                    length = 1;
718                }
719                data[out] = leading_digit_pos;
720                data[out + 1] = Self::POINT_POS;
721                let mut i = 2;
722                while i < length {
723                    data[out + i] = leading_digit_pos + 1 - i as u8;
724                    i += 1;
725                }
726            }
727            let mut i = 0;
728            while i < 4 {
729                data[out + length] = Self::EXP_POS + i;
730                length += 1;
731                i += 1;
732            }
733            data[out + 15] = length as u8;
734            idx += 1;
735        }
736
737        ExpFloatShuffleTable { data }
738    }
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)]
749fn 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    (70 - ((x.to_le() << 1) | 1).leading_zeros() as usize) / 8
763}
764
765// Align data since unaligned access may be slower when crossing a
766// hardware-specific boundary.
767#[repr(C, align(2))]
768struct Digits2([u8; 200]);
769
770static 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)]
781unsafe fn digits2(value: usize) -> &'static u16 {
782    if true {
    if !(value < 100) {
        ::core::panicking::panic("assertion failed: value < 100")
    };
};debug_assert!(value < 100);
783
784    #[allow(clippy::cast_ptr_alignment)]
785    unsafe {
786        &*DIGITS2.0.as_ptr().cast::<u16>().add(value)
787    }
788}
789
790const DIV10K_EXP: i32 = 40;
791const DIV10K_SIG: u32 = ((1u64 << DIV10K_EXP) / 10000 + 1) as u32;
792const NEG10K: u32 = ((1u64 << 32) - 10000) as u32;
793
794const DIV100_EXP: i32 = 19;
795const 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)))]
802const 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)))]
808const 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)))]
813const DIV10_SIG: u32 = (1 << DIV10_EXP) / 10 + 1;
814#[cfg(not(all(target_arch = "x86_64", target_feature = "sse2", not(miri))))]
815const NEG10: u32 = (1 << 8) - 10;
816
817const ZEROS: u64 = 0x0101010101010101 * b'0' as u64;
818
819#[repr(C, align(64))]
820struct 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
873impl Data {
874    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
875    const fn splat64(x: u64) -> u128 {
876        ((x as u128) << 64) | x as u128
877    }
878
879    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
880    const fn splat32(x: u32) -> u128 {
881        Self::splat64(((x as u64) << 32) | x as u64)
882    }
883
884    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
885    const fn splat16(x: u16) -> u128 {
886        Self::splat32(((x as u32) << 16) | x as u32)
887    }
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
905static 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)]
968fn 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]
999fn 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)]
1040fn to_bcd_4x4(y: __m128i, d: &Data) -> __m128i {
1041    unsafe {
1042        let div100 = _mm_load_si128(&raw const d.div100ptr::addr_of!(d.div100).cast::<__m128i>());
1043        let div10 = _mm_load_si128(&raw const d.div10ptr::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            let hundred = _mm_load_si128(&raw const d.hundredptr::addr_of!(d.hundred).cast::<__m128i>());
1061            let moddiv10 = _mm_load_si128(&raw const d.moddiv10ptr::addr_of!(d.moddiv10).cast::<__m128i>());
1062
1063            let y_div_100: __m128i = _mm_srli_epi16(_mm_mulhi_epu16(y, div100), 3);
1064            let y_mod_100: __m128i = _mm_sub_epi16(y, _mm_mullo_epi16(y_div_100, hundred));
1065            let z: __m128i = _mm_or_si128(_mm_slli_epi32(y_mod_100, 16), y_div_100);
1066            _mm_sub_epi16(
1067                _mm_slli_epi16(z, 8),
1068                _mm_mullo_epi16(moddiv10, _mm_mulhi_epu16(z, div10)),
1069            )
1070        }
1071    }
1072}
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)))]
1078struct 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)]
1088fn 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    let bcd = {
1111        // Load constants from memory.
1112        let mut d = &raw const STATIC_DATAptr::addr_of!(STATIC_DATA);
1113        let d = unsafe {
1114            asm!("/*{0}*/", inout(reg) d);
1115            &*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        let abcd_efgh = (abcdefgh << 32)
1121            - ((10000u64 << 32) - 1) * ((abcdefgh * u64::from(DIV10K_SIG)) >> DIV10K_EXP);
1122        let v: __m128i = to_bcd_4x4(_mm_set_epi64x(0, abcd_efgh as i64), d);
1123        (unsafe { _mm_cvtsi128_si64(v) }) as u64
1124    };
1125
1126    BcdResult {
1127        bcd,
1128        len: count_trailing_nonzeros(bcd),
1129    }
1130}
1131
1132struct 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]
1145fn 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        let hi = (value / 100_000_000) as u32;
1191        let lo = (value % 100_000_000) as u32;
1192
1193        unsafe {
1194            let div10k = _mm_load_si128(&raw const d.div10kptr::addr_of!(d.div10k).cast::<__m128i>());
1195            let neg10k = _mm_load_si128(&raw const d.neg10kptr::addr_of!(d.neg10k).cast::<__m128i>());
1196            let x: __m128i = _mm_set_epi64x(i64::from(hi), i64::from(lo));
1197            #[cfg_attr(target_feature = "sse4.1", allow(unused_mut))]
1198            let mut y: __m128i = _mm_add_epi64(
1199                x,
1200                _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            {
1206                y = _mm_shuffle_epi32(y, _MM_SHUFFLE(0, 1, 2, 3));
1207            }
1208
1209            #[cfg_attr(not(target_feature = "sse4.1"), allow(unused_mut))]
1210            let mut bcd: __m128i = to_bcd_4x4(y, d);
1211            let zeros = _mm_load_si128(&raw const d.zerosptr::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            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            let len = if falsecfg!(target_feature = "sse4.1") {
1220                16 - mask.trailing_zeros()
1221            } else {
1222                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            DecDigits {
1234                digits: _mm_or_si128(bcd, zeros),
1235                #[cfg(target_feature = "sse4.1")]
1236                unshuffled: (),
1237                num_digits: len as usize,
1238            }
1239        }
1240    }
1241}
1242
1243#[cfg_attr(feature = "no-panic", no_panic)]
1244#[inline]
1245fn 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        let result = to_bcd8(value);
1296        DecDigits {
1297            digits: result.bcd + ZEROS,
1298            num_digits: result.len,
1299        }
1300    }
1301}
1302
1303#[cfg_attr(feature = "no-panic", no_panic)]
1304unsafe fn write_exp_float_simd_32(
1305    buffer: *mut u8,
1306    dig: &DecDigits<f32>,
1307    last_digit: i32,
1308    has_last_digit: bool,
1309    has_extra_digit: bool,
1310    exp_data: u64,
1311    d: &Data,
1312) -> *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    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    let tail = exp_data | (u64::from(prefix) << 32);
1326    let entry = unsafe {
1327        d.exp_float_shuffles
1328            .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    let length = entry.length as usize - usize::from((exp_data & 0xff000000) == 0);
1354    unsafe { buffer.add(length) }
1355}
1356
1357struct 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]
1369fn to_decimal<Float, UInt>(bin_sig: UInt, raw_exp: i64, regular: bool, d: &Data) -> ToDecimalResult
1370where
1371    Float: FloatTraits,
1372    UInt: traits::UInt,
1373{
1374    let bin_exp = raw_exp - i64::from(Float::EXP_OFFSET);
1375    let num_bits = mem::size_of::<UInt>() as i32 * 8;
1376    const EXTRA_SHIFT: usize = ExpShiftTable::EXTRA_SHIFT;
1377
1378    if !regular {
1379        let dec_exp = compute_dec_exp(bin_exp as i32, false);
1380        let shift = compute_exp_shift(bin_exp as i32, dec_exp + 1).wrapping_add(EXTRA_SHIFT as u8);
1381        let pow10 = unsafe { d.pow10_significands.get_unchecked(-dec_exp - 1) };
1382        let p = umul192_hi128(pow10.hi, pow10.lo, (bin_sig << shift).into());
1383
1384        let mut integral = p.hi >> EXTRA_SHIFT;
1385        let fractional = (p.hi << (64 - EXTRA_SHIFT)) | (p.lo >> EXTRA_SHIFT);
1386
1387        let half_ulp = pow10.hi >> (EXTRA_SHIFT + 1 - shift as usize);
1388        let round_up = half_ulp > u64::MAX - fractional;
1389        let round_down = (half_ulp >> 1) > fractional;
1390        integral += u64::from(round_up);
1391
1392        let mut digit = umul128_add_hi64(fractional, 10, (1 << 63) - 1) as i32;
1393        let lo = umul128_add_hi64(fractional.wrapping_sub(half_ulp >> 1), 10, !0) as i32;
1394        if digit < lo {
1395            digit = lo;
1396        }
1397        return ToDecimalResult {
1398            sig: integral as i64,
1399            exp: dec_exp,
1400            last_digit: digit as u8,
1401            has_last_digit: !(round_up || round_down),
1402        };
1403    }
1404
1405    const LOG10_2_SIG: u64 = 78_913;
1406    const LOG10_2_EXP: i32 = 18;
1407    #[allow(unused_mut)]
1408    let mut dec_exp = if USE_UMUL128_HI64 {
1409        umul128_hi64(bin_exp as u64, LOG10_2_SIG << (64 - LOG10_2_EXP)) as i32
1410    } else {
1411        compute_dec_exp(bin_exp as i32, true)
1412    };
1413    #[cfg(not(miri))]
1414    #[allow(unused_unsafe)]
1415    unsafe {
1416        // Force 32-bit reg for sxtw addressing.
1417        #[cfg(target_arch = "x86_64")]
1418        asm!("/*{0:e}*/", inout(reg) dec_exp);
1419        #[cfg(target_arch = "aarch64")]
1420        asm!("/*{0:w}*/", inout(reg) dec_exp);
1421    }
1422    let mut shift = if ExpShiftTable::ENABLE {
1423        *unsafe {
1424            d.exp_shifts
1425                .data
1426                .get_unchecked((bin_exp + i64::from(f64::EXP_OFFSET)) as usize)
1427        }
1428    } else {
1429        compute_exp_shift(bin_exp as i32, dec_exp + 1).wrapping_add(EXTRA_SHIFT as u8)
1430    };
1431    let even = UInt::from(1) - (bin_sig & UInt::from(1));
1432
1433    if num_bits == 32 {
1434        const EXTRA_SHIFT: usize = 34;
1435        shift += (EXTRA_SHIFT - ExpShiftTable::EXTRA_SHIFT) as u8;
1436        let pow10_hi = unsafe { d.pow10_significands.get_unchecked(-dec_exp - 1) }.hi;
1437        let p = umul128_hi64(pow10_hi + 1, bin_sig.into() << shift);
1438
1439        let mut integral = p >> EXTRA_SHIFT;
1440        let fractional = p & ((1u64 << EXTRA_SHIFT) - 1);
1441
1442        let half_ulp = (pow10_hi >> (65 - shift as usize)) + even.into();
1443        let round_up = ((fractional + half_ulp) >> EXTRA_SHIFT) != 0;
1444        let round_down = half_ulp > fractional;
1445        integral += u64::from(round_up);
1446
1447        let mut digit = ((fractional * 10 + (1u64 << (EXTRA_SHIFT - 1))) >> EXTRA_SHIFT) as i32;
1448        if fractional == (1u64 << (EXTRA_SHIFT - 2)) {
1449            digit = 2; // Round 2.5 to 2.
1450        }
1451        return ToDecimalResult {
1452            sig: integral as i64,
1453            exp: dec_exp,
1454            last_digit: digit as u8,
1455            has_last_digit: !(round_up || round_down),
1456        };
1457    }
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    let pow10 = unsafe { d.pow10_significands.get_unchecked(-dec_exp - 1) };
1483    let p = umul192_hi128(pow10.hi, pow10.lo, (bin_sig << shift).into());
1484
1485    let mut integral = p.hi >> EXTRA_SHIFT;
1486    let fractional = (p.hi << (64 - EXTRA_SHIFT)) | (p.lo >> EXTRA_SHIFT);
1487
1488    let half_ulp = (pow10.hi >> (EXTRA_SHIFT + 1 - shift as usize)) + even.into();
1489    let round_up = fractional.wrapping_add(half_ulp) < fractional;
1490    let round_down = half_ulp > fractional;
1491    integral += u64::from(round_up); // Compute integral before digit.
1492
1493    // Derive the extra digit from the fractional part (parallel with rounding).
1494    let mut digit = umul128_add_hi64(fractional, 10, d.biased_half.get()) as i32;
1495    if fractional == (1u64 << 62) {
1496        digit = 2; // Round 2.5 to 2.
1497    }
1498    ToDecimalResult {
1499        sig: integral as i64,
1500        exp: dec_exp,
1501        last_digit: digit as u8,
1502        has_last_digit: !(round_up || round_down),
1503    }
1504}
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)]
1509unsafe fn write<Float>(value: Float, mut buffer: *mut u8) -> *mut u8
1510where
1511    Float: FloatTraits,
1512{
1513    let bits = value.to_bits();
1514    // It is beneficial to extract exponent and significand early.
1515    let bin_exp = Float::get_exp(bits); // binary exponent
1516    let bin_sig = Float::get_sig(bits); // binary significand
1517
1518    unsafe {
1519        *buffer = b'-';
1520    }
1521    buffer = unsafe { buffer.add(usize::from(Float::is_negative(bits))) };
1522
1523    #[allow(unused_mut)]
1524    let mut d = &raw const STATIC_DATAptr::addr_of!(STATIC_DATA);
1525    let d = unsafe {
1526        // Load constants from memory.
1527        #[cfg(all(any(target_arch = "aarch64", target_arch = "x86_64"), not(miri)))]
1528        asm!("/*{0}*/", inout(reg) d);
1529        &*d
1530    };
1531    let threshold = if Float::NUM_BITS == 64 {
1532        d.threshold.get()
1533    } else {
1534        10_000_000
1535    };
1536
1537    let mut dec;
1538    if bin_exp == 0 {
1539        if bin_sig == Float::SigType::from(0) {
1540            return unsafe {
1541                *buffer = b'0';
1542                *buffer.add(1) = b'.';
1543                *buffer.add(2) = b'0';
1544                buffer.add(3)
1545            };
1546        }
1547        dec = to_decimal::<Float, Float::SigType>(bin_sig, 1, true, d);
1548        let mut dec_sig =
1549            dec.sig * 10 + (-i64::from(dec.has_last_digit) & i64::from(dec.last_digit));
1550        let mut dec_exp = dec.exp;
1551        while dec_sig < threshold as i64 {
1552            dec_sig *= 10;
1553            dec_exp -= 1;
1554        }
1555        let d = div10(dec_sig as u64);
1556        let last_digit = dec_sig - d as i64 * 10;
1557        dec = ToDecimalResult {
1558            sig: d as i64,
1559            exp: dec_exp,
1560            last_digit: last_digit as u8,
1561            has_last_digit: last_digit != 0,
1562        };
1563    } else {
1564        dec = to_decimal::<Float, Float::SigType>(
1565            bin_sig | Float::IMPLICIT_BIT,
1566            bin_exp,
1567            bin_sig != Float::SigType::from(0),
1568            d,
1569        );
1570    }
1571    let mut has_last_digit = dec.has_last_digit;
1572    let has_extra_digit = dec.sig >= threshold as i64;
1573    let mut dec_exp = dec.exp + Float::MAX_DIGITS10 as i32 - 2 + i32::from(has_extra_digit);
1574    if Float::NUM_BITS == 32 && dec.sig < 1_000_000 {
1575        dec.sig = 10 * dec.sig + (-i64::from(has_last_digit) & i64::from(dec.last_digit));
1576        has_last_digit = false;
1577        dec_exp -= 1;
1578    }
1579
1580    // Write significand.
1581    let dig = Float::to_digits(dec.sig as u64, d);
1582
1583    if Float::NUM_BITS == 32
1584        && ExpFloatShuffleTable::ENABLE
1585        && !Float::FIXED_DEC_EXP.contains(&dec_exp)
1586    {
1587        unsafe {
1588            let exp_data = *d
1589                .exp_strings
1590                .data
1591                .get_unchecked((dec_exp + ExpStringTable::OFFSET) as usize);
1592            return Float::write_exp_float_simd(
1593                buffer,
1594                &dig,
1595                i32::from(dec.last_digit),
1596                has_last_digit,
1597                has_extra_digit,
1598                exp_data,
1599                d,
1600            );
1601        }
1602    }
1603
1604    let bcd_size = if Float::NUM_BITS == 64 { 16 } else { 8 };
1605    unsafe {
1606        buffer
1607            .add(usize::from(has_extra_digit))
1608            .cast::<Float::DecDigitsType>()
1609            .write_unaligned(dig.digits);
1610        buffer
1611            .add(usize::from(has_extra_digit) + bcd_size)
1612            .write(b'0' + dec.last_digit);
1613    }
1614    let length = usize::from(has_extra_digit)
1615        + if has_last_digit {
1616            bcd_size + 1
1617        } else {
1618            dig.num_digits
1619        }
1620        - 1;
1621
1622    if Float::FIXED_DEC_EXP.contains(&dec_exp) {
1623        if length as i32 - 1 <= dec_exp {
1624            // 1234e7 -> 12340000000.0
1625            return unsafe {
1626                ptr::copy(buffer.add(1), buffer, length);
1627                ptr::write_bytes(buffer.add(length), b'0', dec_exp as usize + 3 - length);
1628                *buffer.add(dec_exp as usize + 1) = b'.';
1629                buffer.add(dec_exp as usize + 3)
1630            };
1631        } else if 0 <= dec_exp {
1632            // 1234e-2 -> 12.34
1633            return unsafe {
1634                ptr::copy(buffer.add(1), buffer, dec_exp as usize + 1);
1635                *buffer.add(dec_exp as usize + 1) = b'.';
1636                buffer.add(length + 1)
1637            };
1638        } else {
1639            // 1234e-6 -> 0.001234
1640            return unsafe {
1641                ptr::copy(buffer.add(1), buffer.add((1 - dec_exp) as usize), length);
1642                ptr::write_bytes(buffer, b'0', (1 - dec_exp) as usize);
1643                *buffer.add(1) = b'.';
1644                buffer.add((1 - dec_exp) as usize + length)
1645            };
1646        }
1647    }
1648
1649    unsafe {
1650        // 1234e30 -> 1.234e33
1651        *buffer = *buffer.add(1);
1652        *buffer.add(1) = b'.';
1653    }
1654    buffer = unsafe { buffer.add(length + usize::from(length > 1)) };
1655
1656    // Write exponent.
1657    if ExpStringTable::ENABLE {
1658        let mut exp_data = unsafe {
1659            *d.exp_strings
1660                .data
1661                .get_unchecked((dec_exp + ExpStringTable::OFFSET) as usize)
1662        };
1663        let len = (exp_data >> 48) as usize;
1664        exp_data = exp_data.to_le();
1665        unsafe {
1666            ptr::copy_nonoverlapping(
1667                &raw const exp_dataptr::addr_of!(exp_data).cast::<u8>(),
1668                buffer,
1669                if Float::MAX_10_EXP >= 100 { 5 } else { 4 },
1670            );
1671            return buffer.add(len);
1672        }
1673    }
1674    let sign_ptr = buffer;
1675    let e_sign = if dec_exp >= 0 {
1676        (u16::from(b'+') << 8) | u16::from(b'e')
1677    } else {
1678        (u16::from(b'-') << 8) | u16::from(b'e')
1679    };
1680    buffer = unsafe { buffer.add(1) };
1681    dec_exp = if dec_exp >= 0 { dec_exp } else { -dec_exp };
1682    buffer = unsafe { buffer.add(usize::from(dec_exp >= 10)) };
1683    if Float::MAX_10_EXP >= 100 {
1684        // digit = dec_exp / 100
1685        let digit = if USE_UMUL128_HI64 {
1686            umul128_hi64(dec_exp as u64, 0x290000000000000) as u32
1687        } else {
1688            (dec_exp as u32 * DIV100_SIG) >> DIV100_EXP
1689        };
1690        unsafe {
1691            *buffer = b'0' + digit as u8;
1692        }
1693        buffer = unsafe { buffer.add(usize::from(dec_exp >= 100)) };
1694        dec_exp -= (digit * 100) as i32;
1695    }
1696    unsafe {
1697        buffer
1698            .cast::<u16>()
1699            .write_unaligned(*digits2(dec_exp as usize));
1700        sign_ptr.cast::<u16>().write_unaligned(e_sign.to_le());
1701        buffer.add(2)
1702    }
1703}
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/// ```
1714pub struct Buffer {
1715    bytes: [MaybeUninit<u8>; BUFFER_SIZE],
1716}
1717
1718impl 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    pub fn new() -> Self {
1724        let bytes = [MaybeUninit::<u8>::uninit(); BUFFER_SIZE];
1725        Buffer { bytes }
1726    }
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    pub fn format<F: Float>(&mut self, f: F) -> &str {
1741        if f.is_nonfinite() {
1742            f.format_nonfinite()
1743        } else {
1744            self.format_finite(f)
1745        }
1746    }
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    pub fn format_finite<F: Float>(&mut self, f: F) -> &str {
1765        unsafe {
1766            let end = f.write_to_zmij_buffer(self.bytes.as_mut_ptr().cast::<u8>());
1767            let len = end.offset_from(self.bytes.as_ptr().cast::<u8>()) as usize;
1768            let slice = slice::from_raw_parts(self.bytes.as_ptr().cast::<u8>(), len);
1769            str::from_utf8_unchecked(slice)
1770        }
1771    }
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)]
1781pub trait Float: private::Sealed {}
1782impl Float for f32 {}
1783impl Float for f64 {}
1784
1785mod 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        fn is_nonfinite(self) -> bool {
1795            const EXP_MASK: u32 = 0x7f800000;
1796            let bits = self.to_bits();
1797            bits & EXP_MASK == EXP_MASK
1798        }
1799
1800        #[cold]
1801        #[cfg_attr(feature = "no-panic", inline)]
1802        fn format_nonfinite(self) -> &'static str {
1803            const MANTISSA_MASK: u32 = 0x007fffff;
1804            const SIGN_MASK: u32 = 0x80000000;
1805            let bits = self.to_bits();
1806            if bits & MANTISSA_MASK != 0 {
1807                crate::NAN
1808            } else if bits & SIGN_MASK != 0 {
1809                crate::NEG_INFINITY
1810            } else {
1811                crate::INFINITY
1812            }
1813        }
1814
1815        #[cfg_attr(feature = "no-panic", inline)]
1816        unsafe fn write_to_zmij_buffer(self, buffer: *mut u8) -> *mut u8 {
1817            unsafe { crate::write(self, buffer) }
1818        }
1819    }
1820
1821    impl Sealed for f64 {
1822        #[inline]
1823        fn is_nonfinite(self) -> bool {
1824            const EXP_MASK: u64 = 0x7ff0000000000000;
1825            let bits = self.to_bits();
1826            bits & EXP_MASK == EXP_MASK
1827        }
1828
1829        #[cold]
1830        #[cfg_attr(feature = "no-panic", inline)]
1831        fn format_nonfinite(self) -> &'static str {
1832            const MANTISSA_MASK: u64 = 0x000fffffffffffff;
1833            const SIGN_MASK: u64 = 0x8000000000000000;
1834            let bits = self.to_bits();
1835            if bits & MANTISSA_MASK != 0 {
1836                crate::NAN
1837            } else if bits & SIGN_MASK != 0 {
1838                crate::NEG_INFINITY
1839            } else {
1840                crate::INFINITY
1841            }
1842        }
1843
1844        #[cfg_attr(feature = "no-panic", inline)]
1845        unsafe fn write_to_zmij_buffer(self, buffer: *mut u8) -> *mut u8 {
1846            unsafe { crate::write(self, buffer) }
1847        }
1848    }
1849}
1850
1851impl Default for Buffer {
1852    #[inline]
1853    #[cfg_attr(feature = "no-panic", no_panic)]
1854    fn default() -> Self {
1855        Buffer::new()
1856    }
1857}