Skip to main content

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