Coverage Report

Created: 2026-01-17 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/zmij-1.0.14/src/lib.rs
Line
Count
Source
1
//! [![github]](https://github.com/dtolnay/zmij) [![crates-io]](https://crates.io/crates/zmij) [![docs-rs]](https://docs.rs/zmij)
2
//!
3
//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4
//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5
//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6
//!
7
//! <br>
8
//!
9
//! A double-to-string conversion algorithm based on [Schubfach] and [yy].
10
//!
11
//! This Rust implementation is a line-by-line port of Victor Zverovich's
12
//! implementation in C++, <https://github.com/vitaut/zmij>.
13
//!
14
//! [Schubfach]: https://fmt.dev/papers/Schubfach4.pdf
15
//! [yy]: https://github.com/ibireme/c_numconv_benchmark/blob/master/vendor/yy_double/yy_double.c
16
//!
17
//! <br>
18
//!
19
//! # Example
20
//!
21
//! ```
22
//! fn main() {
23
//!     let mut buffer = zmij::Buffer::new();
24
//!     let printed = buffer.format(1.234);
25
//!     assert_eq!(printed, "1.234");
26
//! }
27
//! ```
28
//!
29
//! <br>
30
//!
31
//! ## Performance
32
//!
33
//! The [dtoa-benchmark] compares this library and other Rust floating point
34
//! formatting implementations across a range of precisions. The vertical axis
35
//! in this chart shows nanoseconds taken by a single execution of
36
//! `zmij::Buffer::new().format_finite(value)` so a lower result indicates a
37
//! faster library.
38
//!
39
//! [dtoa-benchmark]: https://github.com/dtolnay/dtoa-benchmark
40
//!
41
//! ![performance](https://raw.githubusercontent.com/dtolnay/zmij/master/dtoa-benchmark.png)
42
43
#![no_std]
44
#![doc(html_root_url = "https://docs.rs/zmij/1.0.14")]
45
#![deny(unsafe_op_in_unsafe_fn)]
46
#![allow(non_camel_case_types, non_snake_case)]
47
#![allow(
48
    clippy::blocks_in_conditions,
49
    clippy::cast_possible_truncation,
50
    clippy::cast_possible_wrap,
51
    clippy::cast_ptr_alignment,
52
    clippy::cast_sign_loss,
53
    clippy::doc_markdown,
54
    clippy::incompatible_msrv,
55
    clippy::items_after_statements,
56
    clippy::must_use_candidate,
57
    clippy::needless_doctest_main,
58
    clippy::never_loop,
59
    clippy::redundant_else,
60
    clippy::similar_names,
61
    clippy::too_many_arguments,
62
    clippy::too_many_lines,
63
    clippy::unreadable_literal,
64
    clippy::used_underscore_items,
65
    clippy::while_immutable_condition,
66
    clippy::wildcard_imports
67
)]
68
69
#[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
70
mod stdarch_x86;
71
#[cfg(test)]
72
mod tests;
73
mod traits;
74
75
#[cfg(all(any(target_arch = "aarch64", target_arch = "x86_64"), not(miri)))]
76
use core::arch::asm;
77
#[cfg(not(zmij_no_select_unpredictable))]
78
use core::hint;
79
use core::mem::{self, MaybeUninit};
80
use core::ptr;
81
use core::slice;
82
use core::str;
83
#[cfg(feature = "no-panic")]
84
use no_panic::no_panic;
85
86
const BUFFER_SIZE: usize = 24;
87
const NAN: &str = "NaN";
88
const INFINITY: &str = "inf";
89
const NEG_INFINITY: &str = "-inf";
90
91
// A decimal floating-point number sig * pow(10, exp).
92
// If exp is non_finite_exp then the number is a NaN or an infinity.
93
struct dec_fp {
94
    sig: i64, // significand
95
    exp: i32, // exponent
96
}
97
98
#[cfg_attr(test, derive(Debug, PartialEq))]
99
struct uint128 {
100
    hi: u64,
101
    lo: u64,
102
}
103
104
// Use umul128_hi64 for division.
105
const USE_UMUL128_HI64: bool = cfg!(target_vendor = "apple");
106
107
// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
108
0
const fn umul128(x: u64, y: u64) -> u128 {
109
0
    x as u128 * y as u128
110
0
}
111
112
0
const fn umul128_hi64(x: u64, y: u64) -> u64 {
113
0
    (umul128(x, y) >> 64) as u64
114
0
}
115
116
#[cfg_attr(feature = "no-panic", no_panic)]
117
0
fn umul192_hi128(x_hi: u64, x_lo: u64, y: u64) -> uint128 {
118
0
    let p = umul128(x_hi, y);
119
0
    let lo = (p as u64).wrapping_add((umul128(x_lo, y) >> 64) as u64);
120
0
    uint128 {
121
0
        hi: (p >> 64) as u64 + u64::from(lo < p as u64),
122
0
        lo,
123
0
    }
124
0
}
125
126
// Computes high 64 bits of multiplication of x and y, discards the least
127
// significant bit and rounds to odd, where x = uint128_t(x_hi << 64) | x_lo.
128
#[cfg_attr(feature = "no-panic", no_panic)]
129
0
fn umulhi_inexact_to_odd<UInt>(x_hi: u64, x_lo: u64, y: UInt) -> UInt
130
0
where
131
0
    UInt: traits::UInt,
132
{
133
0
    let num_bits = mem::size_of::<UInt>() * 8;
134
0
    if num_bits == 64 {
135
0
        let p = umul192_hi128(x_hi, x_lo, y.into());
136
0
        UInt::truncate(p.hi | u64::from((p.lo >> 1) != 0))
137
    } else {
138
0
        let p = (umul128(x_hi, y.into()) >> 32) as u64;
139
0
        UInt::enlarge((p >> 32) as u32 | u32::from((p as u32 >> 1) != 0))
140
    }
141
0
}
Unexecuted instantiation: zmij::umulhi_inexact_to_odd::<u32>
Unexecuted instantiation: zmij::umulhi_inexact_to_odd::<u64>
142
143
trait FloatTraits: traits::Float {
144
    const NUM_BITS: i32;
145
    const NUM_SIG_BITS: i32 = Self::MANTISSA_DIGITS as i32 - 1;
146
    const NUM_EXP_BITS: i32 = Self::NUM_BITS - Self::NUM_SIG_BITS - 1;
147
    const EXP_MASK: i32 = (1 << Self::NUM_EXP_BITS) - 1;
148
    const EXP_BIAS: i32 = (1 << (Self::NUM_EXP_BITS - 1)) - 1;
149
150
    type SigType: traits::UInt;
151
    const IMPLICIT_BIT: Self::SigType;
152
153
    fn to_bits(self) -> Self::SigType;
154
155
0
    fn is_negative(bits: Self::SigType) -> bool {
156
0
        (bits >> (Self::NUM_BITS - 1)) != Self::SigType::from(0)
157
0
    }
Unexecuted instantiation: <f64 as zmij::FloatTraits>::is_negative
Unexecuted instantiation: <f32 as zmij::FloatTraits>::is_negative
158
159
0
    fn get_sig(bits: Self::SigType) -> Self::SigType {
160
0
        bits & (Self::IMPLICIT_BIT - Self::SigType::from(1))
161
0
    }
Unexecuted instantiation: <f64 as zmij::FloatTraits>::get_sig
Unexecuted instantiation: <f32 as zmij::FloatTraits>::get_sig
162
163
0
    fn get_exp(bits: Self::SigType) -> i64 {
164
0
        (bits >> Self::NUM_SIG_BITS).into() as i64 & i64::from(Self::EXP_MASK)
165
0
    }
Unexecuted instantiation: <f64 as zmij::FloatTraits>::get_exp
Unexecuted instantiation: <f32 as zmij::FloatTraits>::get_exp
166
}
167
168
impl FloatTraits for f32 {
169
    const NUM_BITS: i32 = 32;
170
    const IMPLICIT_BIT: u32 = 1 << Self::NUM_SIG_BITS;
171
172
    type SigType = u32;
173
174
0
    fn to_bits(self) -> Self::SigType {
175
0
        self.to_bits()
176
0
    }
177
}
178
179
impl FloatTraits for f64 {
180
    const NUM_BITS: i32 = 64;
181
    const IMPLICIT_BIT: u64 = 1 << Self::NUM_SIG_BITS;
182
183
    type SigType = u64;
184
185
0
    fn to_bits(self) -> Self::SigType {
186
0
        self.to_bits()
187
0
    }
188
}
189
190
struct Pow10SignificandsTable {
191
    data: [u64; Self::NUM_POW10 * 2],
192
}
193
194
impl Pow10SignificandsTable {
195
    const SPLIT_TABLES: bool = cfg!(target_arch = "aarch64");
196
    const NUM_POW10: usize = 617;
197
198
0
    unsafe fn get_unchecked(&self, dec_exp: i32) -> uint128 {
199
        const DEC_EXP_MIN: i32 = -292;
200
0
        if !Self::SPLIT_TABLES {
201
0
            let index = ((dec_exp - DEC_EXP_MIN) * 2) as usize;
202
0
            return uint128 {
203
0
                hi: unsafe { *self.data.get_unchecked(index) },
204
0
                lo: unsafe { *self.data.get_unchecked(index + 1) },
205
0
            };
206
0
        }
207
208
        unsafe {
209
            #[cfg_attr(
210
                not(all(any(target_arch = "x86_64", target_arch = "aarch64"), not(miri))),
211
                allow(unused_mut)
212
            )]
213
0
            let mut hi = self
214
0
                .data
215
0
                .as_ptr()
216
0
                .offset(Self::NUM_POW10 as isize + DEC_EXP_MIN as isize - 1);
217
            #[cfg_attr(
218
                not(all(any(target_arch = "x86_64", target_arch = "aarch64"), not(miri))),
219
                allow(unused_mut)
220
            )]
221
0
            let mut lo = hi.add(Self::NUM_POW10);
222
223
            // Force indexed loads.
224
            #[cfg(all(any(target_arch = "x86_64", target_arch = "aarch64"), not(miri)))]
225
0
            asm!("/*{0}{1}*/", inout(reg) hi, inout(reg) lo);
226
0
            uint128 {
227
0
                hi: *hi.offset(-dec_exp as isize),
228
0
                lo: *lo.offset(-dec_exp as isize),
229
0
            }
230
        }
231
0
    }
232
233
    #[cfg(test)]
234
    fn get(&self, dec_exp: i32) -> uint128 {
235
        const DEC_EXP_MIN: i32 = -292;
236
        assert!((DEC_EXP_MIN..DEC_EXP_MIN + Self::NUM_POW10 as i32).contains(&dec_exp));
237
        unsafe { self.get_unchecked(dec_exp) }
238
    }
239
}
240
241
// 128-bit significands of powers of 10 rounded down.
242
// Generated using 192-bit arithmetic method by Dougall Johnson.
243
static POW10_SIGNIFICANDS: Pow10SignificandsTable = {
244
    let mut data = [0; Pow10SignificandsTable::NUM_POW10 * 2];
245
246
    struct uint192 {
247
        w0: u64, // least significant
248
        w1: u64,
249
        w2: u64, // most significant
250
    }
251
252
    // first element, rounded up to cancel out rounding down in the
253
    // multiplication, and minimise significant bits
254
    let mut current = uint192 {
255
        w0: 0xe000000000000000,
256
        w1: 0x25e8e89c13bb0f7a,
257
        w2: 0xff77b1fcbebcdc4f,
258
    };
259
    let ten = 0xa000000000000000;
260
    let mut i = 0;
261
    while i < Pow10SignificandsTable::NUM_POW10 {
262
        if Pow10SignificandsTable::SPLIT_TABLES {
263
            data[Pow10SignificandsTable::NUM_POW10 - i - 1] = current.w2;
264
            data[Pow10SignificandsTable::NUM_POW10 * 2 - i - 1] = current.w1;
265
        } else {
266
            data[i * 2] = current.w2;
267
            data[i * 2 + 1] = current.w1;
268
        }
269
270
        let h0: u64 = umul128_hi64(current.w0, ten);
271
        let h1: u64 = umul128_hi64(current.w1, ten);
272
273
        let c0: u64 = h0.wrapping_add(current.w1.wrapping_mul(ten));
274
        let c1: u64 = ((c0 < h0) as u64 + h1).wrapping_add(current.w2.wrapping_mul(ten));
275
        let c2: u64 = (c1 < h1) as u64 + umul128_hi64(current.w2, ten); // dodgy carry
276
277
        // normalise
278
        if (c2 >> 63) != 0 {
279
            current = uint192 {
280
                w0: c0,
281
                w1: c1,
282
                w2: c2,
283
            };
284
        } else {
285
            current = uint192 {
286
                w0: c0 << 1,
287
                w1: c1 << 1 | c0 >> 63,
288
                w2: c2 << 1 | c1 >> 63,
289
            };
290
        }
291
292
        i += 1;
293
    }
294
295
    Pow10SignificandsTable { data }
296
};
297
298
// Computes the decimal exponent as floor(log10(2**bin_exp)) if regular or
299
// floor(log10(3/4 * 2**bin_exp)) otherwise, without branching.
300
0
const fn compute_dec_exp(bin_exp: i32, regular: bool) -> i32 {
301
0
    debug_assert!(bin_exp >= -1334 && bin_exp <= 2620);
302
    // log10_3_over_4_sig = -log10(3/4) * 2**log10_2_exp rounded to a power of 2
303
    const LOG10_3_OVER_4_SIG: i32 = 131_072;
304
    // log10_2_sig = round(log10(2) * 2**log10_2_exp)
305
    const LOG10_2_SIG: i32 = 315_653;
306
    const LOG10_2_EXP: i32 = 20;
307
0
    (bin_exp * LOG10_2_SIG - !regular as i32 * LOG10_3_OVER_4_SIG) >> LOG10_2_EXP
308
0
}
309
310
0
const fn do_compute_exp_shift(bin_exp: i32, dec_exp: i32) -> u8 {
311
0
    debug_assert!(dec_exp >= -350 && dec_exp <= 350);
312
    // log2_pow10_sig = round(log2(10) * 2**log2_pow10_exp) + 1
313
    const LOG2_POW10_SIG: i32 = 217_707;
314
    const LOG2_POW10_EXP: i32 = 16;
315
    // pow10_bin_exp = floor(log2(10**-dec_exp))
316
0
    let pow10_bin_exp = (-dec_exp * LOG2_POW10_SIG) >> LOG2_POW10_EXP;
317
    // pow10 = ((pow10_hi << 64) | pow10_lo) * 2**(pow10_bin_exp - 127)
318
0
    (bin_exp + pow10_bin_exp + 1) as u8
319
0
}
320
321
struct ExpShiftTable {
322
    data: [u8; if Self::ENABLE {
323
        Self::NUM_EXPS as usize
324
    } else {
325
        1
326
    }],
327
}
328
329
impl ExpShiftTable {
330
    const ENABLE: bool = true;
331
    const NUM_EXPS: i32 = f64::EXP_MASK + 1;
332
    const OFFSET: i32 = f64::NUM_SIG_BITS + f64::EXP_BIAS;
333
}
334
335
static EXP_SHIFTS: ExpShiftTable = {
336
    let mut data = [0u8; if ExpShiftTable::ENABLE {
337
        ExpShiftTable::NUM_EXPS as usize
338
    } else {
339
        1
340
    }];
341
342
    if ExpShiftTable::ENABLE {
343
        let mut raw_exp = 0;
344
        while raw_exp < ExpShiftTable::NUM_EXPS {
345
            let mut bin_exp = raw_exp - ExpShiftTable::OFFSET;
346
            if raw_exp == 0 {
347
                bin_exp += 1;
348
            }
349
            let dec_exp = compute_dec_exp(bin_exp, true);
350
            data[raw_exp as usize] = do_compute_exp_shift(bin_exp, dec_exp) as u8;
351
            raw_exp += 1;
352
        }
353
    }
354
355
    ExpShiftTable { data }
356
};
357
358
// Computes a shift so that, after scaling by a power of 10, the intermediate
359
// result always has a fixed 128-bit fractional part (for double).
360
//
361
// Different binary exponents can map to the same decimal exponent, but place
362
// the decimal point at different bit positions. The shift compensates for this.
363
//
364
// For example, both 3 * 2**59 and 3 * 2**60 have dec_exp = 2, but dividing by
365
// 10^dec_exp puts the decimal point in different bit positions:
366
//   3 * 2**59 / 100 = 1.72...e+16  (needs shift = 1 + 1)
367
//   3 * 2**60 / 100 = 3.45...e+16  (needs shift = 2 + 1)
368
0
unsafe fn compute_exp_shift<UInt, const ONLY_REGULAR: bool>(bin_exp: i32, dec_exp: i32) -> u8
369
0
where
370
0
    UInt: traits::UInt,
371
{
372
0
    let num_bits = mem::size_of::<UInt>() * 8;
373
0
    if num_bits == 64 && ExpShiftTable::ENABLE && ONLY_REGULAR {
374
        unsafe {
375
0
            *EXP_SHIFTS
376
0
                .data
377
0
                .as_ptr()
378
0
                .add((bin_exp + ExpShiftTable::OFFSET) as usize)
379
        }
380
    } else {
381
0
        do_compute_exp_shift(bin_exp, dec_exp)
382
    }
383
0
}
Unexecuted instantiation: zmij::compute_exp_shift::<u32, false>
Unexecuted instantiation: zmij::compute_exp_shift::<u32, true>
Unexecuted instantiation: zmij::compute_exp_shift::<u64, false>
Unexecuted instantiation: zmij::compute_exp_shift::<u64, true>
384
385
#[cfg_attr(feature = "no-panic", no_panic)]
386
0
fn count_trailing_nonzeros(x: u64) -> usize {
387
    // We count the number of bytes until there are only zeros left.
388
    // The code is equivalent to
389
    //    8 - x.leading_zeros() / 8
390
    // but if the BSR instruction is emitted (as gcc on x64 does with default
391
    // settings), subtracting the constant before dividing allows the compiler
392
    // to combine it with the subtraction which it inserts due to BSR counting
393
    // in the opposite direction.
394
    //
395
    // Additionally, the BSR instruction requires a zero check. Since the high
396
    // bit is unused we can avoid the zero check by shifting the datum left by
397
    // one and inserting a sentinel bit at the end. This can be faster than the
398
    // automatically inserted range check.
399
0
    (70 - ((x.to_le() << 1) | 1).leading_zeros()) as usize / 8
400
0
}
401
402
// Align data since unaligned access may be slower when crossing a
403
// hardware-specific boundary.
404
#[repr(C, align(2))]
405
struct Digits2([u8; 200]);
406
407
static DIGITS2: Digits2 = Digits2(
408
    *b"0001020304050607080910111213141516171819\
409
       2021222324252627282930313233343536373839\
410
       4041424344454647484950515253545556575859\
411
       6061626364656667686970717273747576777879\
412
       8081828384858687888990919293949596979899",
413
);
414
415
// Converts value in the range [0, 100) to a string. GCC generates a bit better
416
// code when value is pointer-size (https://www.godbolt.org/z/5fEPMT1cc).
417
#[cfg_attr(feature = "no-panic", no_panic)]
418
0
unsafe fn digits2(value: usize) -> &'static u16 {
419
0
    debug_assert!(value < 100);
420
421
    #[allow(clippy::cast_ptr_alignment)]
422
    unsafe {
423
0
        &*DIGITS2.0.as_ptr().cast::<u16>().add(value)
424
    }
425
0
}
426
427
const DIV10K_EXP: i32 = 40;
428
const DIV10K_SIG: u32 = ((1u64 << DIV10K_EXP) / 10000 + 1) as u32;
429
const NEG10K: u32 = ((1u64 << 32) - 10000) as u32;
430
const DIV100_EXP: i32 = 19;
431
const DIV100_SIG: u32 = (1 << DIV100_EXP) / 100 + 1;
432
const NEG100: u32 = (1 << 16) - 100;
433
const DIV10_EXP: i32 = 10;
434
const DIV10_SIG: u32 = (1 << DIV10_EXP) / 10 + 1;
435
const NEG10: u32 = (1 << 8) - 10;
436
437
const ZEROS: u64 = 0x0101010101010101 * b'0' as u64;
438
439
#[cfg_attr(feature = "no-panic", no_panic)]
440
0
fn to_bcd8(abcdefgh: u64) -> u64 {
441
    // An optimization from Xiang JunBo.
442
    // Three steps BCD. Base 10000 -> base 100 -> base 10.
443
    // div and mod are evaluated simultaneously as, e.g.
444
    //   (abcdefgh / 10000) << 32 + (abcdefgh % 10000)
445
    //      == abcdefgh + (2**32 - 10000) * (abcdefgh / 10000)))
446
    // where the division on the RHS is implemented by the usual multiply + shift
447
    // trick and the fractional bits are masked away.
448
0
    let abcd_efgh =
449
0
        abcdefgh + u64::from(NEG10K) * ((abcdefgh * u64::from(DIV10K_SIG)) >> DIV10K_EXP);
450
0
    let ab_cd_ef_gh = abcd_efgh
451
0
        + u64::from(NEG100) * (((abcd_efgh * u64::from(DIV100_SIG)) >> DIV100_EXP) & 0x7f0000007f);
452
0
    let a_b_c_d_e_f_g_h = ab_cd_ef_gh
453
0
        + u64::from(NEG10)
454
0
            * (((ab_cd_ef_gh * u64::from(DIV10_SIG)) >> DIV10_EXP) & 0xf000f000f000f);
455
0
    a_b_c_d_e_f_g_h.to_be()
456
0
}
457
458
0
unsafe fn write_if(buffer: *mut u8, digit: u32, condition: bool) -> *mut u8 {
459
    unsafe {
460
0
        *buffer = b'0' + digit as u8;
461
0
        buffer.add(usize::from(condition))
462
    }
463
0
}
464
465
0
unsafe fn write8(buffer: *mut u8, value: u64) {
466
0
    unsafe {
467
0
        buffer.cast::<u64>().write_unaligned(value);
468
0
    }
469
0
}
470
471
// Writes a significand consisting of up to 17 decimal digits (16-17 for
472
// normals) and removes trailing zeros.
473
#[cfg_attr(feature = "no-panic", no_panic)]
474
0
unsafe fn write_significand17(mut buffer: *mut u8, value: u64, has17digits: bool) -> *mut u8 {
475
    #[cfg(all(target_arch = "aarch64", target_feature = "neon", not(miri)))]
476
    {
477
        // An optimized version for NEON by Dougall Johnson.
478
479
        use core::arch::aarch64::*;
480
481
        const NEG10K: i32 = -10000 + 0x10000;
482
483
        struct ToStringConstants {
484
            mul_const: u64,
485
            hundred_million: u64,
486
            multipliers32: int32x4_t,
487
            multipliers16: int16x8_t,
488
        }
489
490
        static CONSTANTS: ToStringConstants = ToStringConstants {
491
            mul_const: 0xabcc77118461cefd,
492
            hundred_million: 100000000,
493
            multipliers32: unsafe {
494
                mem::transmute::<[i32; 4], int32x4_t>([
495
                    DIV10K_SIG as i32,
496
                    NEG10K,
497
                    (DIV100_SIG << 12) as i32,
498
                    NEG100 as i32,
499
                ])
500
            },
501
            multipliers16: unsafe {
502
                mem::transmute::<[i16; 8], int16x8_t>([0xce0, NEG10 as i16, 0, 0, 0, 0, 0, 0])
503
            },
504
        };
505
506
        let mut c = ptr::addr_of!(CONSTANTS);
507
508
        // Compiler barrier, or clang doesn't load from memory and generates 15
509
        // more instructions
510
        let c = unsafe {
511
            asm!("/*{0}*/", inout(reg) c);
512
            &*c
513
        };
514
515
        let mut hundred_million = c.hundred_million;
516
517
        // Compiler barrier, or clang narrows the load to 32-bit and unpairs it.
518
        unsafe {
519
            asm!("/*{0}*/", inout(reg) hundred_million);
520
        }
521
522
        // Equivalent to abbccddee = value / 100000000, ffgghhii = value % 100000000.
523
        let abbccddee = (umul128(value, c.mul_const) >> 90) as u64;
524
        let ffgghhii = value - abbccddee * hundred_million;
525
526
        // We could probably make this bit faster, but we're preferring to
527
        // reuse the constants for now.
528
        let a = (umul128(abbccddee, c.mul_const) >> 90) as u64;
529
        let bbccddee = abbccddee - a * hundred_million;
530
531
        buffer = unsafe { write_if(buffer, a as u32, has17digits) };
532
533
        unsafe {
534
            let ffgghhii_bbccddee_64: uint64x1_t =
535
                mem::transmute::<u64, uint64x1_t>((ffgghhii << 32) | bbccddee);
536
            let bbccddee_ffgghhii: int32x2_t = vreinterpret_s32_u64(ffgghhii_bbccddee_64);
537
538
            let bbcc_ffgg: int32x2_t = vreinterpret_s32_u32(vshr_n_u32(
539
                vreinterpret_u32_s32(vqdmulh_n_s32(
540
                    bbccddee_ffgghhii,
541
                    mem::transmute::<int32x4_t, [i32; 4]>(c.multipliers32)[0],
542
                )),
543
                9,
544
            ));
545
            let ddee_bbcc_hhii_ffgg_32: int32x2_t = vmla_n_s32(
546
                bbccddee_ffgghhii,
547
                bbcc_ffgg,
548
                mem::transmute::<int32x4_t, [i32; 4]>(c.multipliers32)[1],
549
            );
550
551
            let mut ddee_bbcc_hhii_ffgg: int32x4_t =
552
                vreinterpretq_s32_u32(vshll_n_u16(vreinterpret_u16_s32(ddee_bbcc_hhii_ffgg_32), 0));
553
554
            // Compiler barrier, or clang breaks the subsequent MLA into UADDW +
555
            // MUL.
556
            asm!("/*{:v}*/", inout(vreg) ddee_bbcc_hhii_ffgg);
557
558
            let dd_bb_hh_ff: int32x4_t = vqdmulhq_n_s32(
559
                ddee_bbcc_hhii_ffgg,
560
                mem::transmute::<int32x4_t, [i32; 4]>(c.multipliers32)[2],
561
            );
562
            let ee_dd_cc_bb_ii_hh_gg_ff: int16x8_t = vreinterpretq_s16_s32(vmlaq_n_s32(
563
                ddee_bbcc_hhii_ffgg,
564
                dd_bb_hh_ff,
565
                mem::transmute::<int32x4_t, [i32; 4]>(c.multipliers32)[3],
566
            ));
567
            let high_10s: int16x8_t = vqdmulhq_n_s16(
568
                ee_dd_cc_bb_ii_hh_gg_ff,
569
                mem::transmute::<int16x8_t, [i16; 8]>(c.multipliers16)[0],
570
            );
571
            let digits: uint8x16_t = vrev64q_u8(vreinterpretq_u8_s16(vmlaq_n_s16(
572
                ee_dd_cc_bb_ii_hh_gg_ff,
573
                high_10s,
574
                mem::transmute::<int16x8_t, [i16; 8]>(c.multipliers16)[1],
575
            )));
576
            let str: uint16x8_t = vaddq_u16(
577
                vreinterpretq_u16_u8(digits),
578
                vreinterpretq_u16_s8(vdupq_n_s8(b'0' as i8)),
579
            );
580
581
            buffer.cast::<uint16x8_t>().write_unaligned(str);
582
583
            let is_zero: uint16x8_t = vreinterpretq_u16_u8(vceqq_u8(digits, vdupq_n_u8(0)));
584
            let zeros: u64 = !vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(is_zero, 4)), 0);
585
586
            buffer.add(16 - (zeros.leading_zeros() as usize >> 2))
587
        }
588
    }
589
590
    #[cfg(all(target_arch = "x86_64", target_feature = "sse2", not(miri)))]
591
    {
592
        use crate::stdarch_x86::*;
593
594
0
        let abbccddee = (value / 100_000_000) as u32;
595
0
        let ffgghhii = (value % 100_000_000) as u32;
596
0
        let a = abbccddee / 100_000_000;
597
0
        let bbccddee = abbccddee % 100_000_000;
598
599
0
        buffer = unsafe { write_if(buffer, a, has17digits) };
600
601
        #[repr(C, align(64))]
602
        struct C {
603
            div10k: __m128i,
604
            divmod10k: __m128i,
605
            div100: __m128i,
606
            divmod100: __m128i,
607
            div10: __m128i,
608
            #[cfg(target_feature = "sse4.1")]
609
            neg10: __m128i,
610
            #[cfg(target_feature = "sse4.1")]
611
            bswap: __m128i,
612
            #[cfg(not(target_feature = "sse4.1"))]
613
            hundred: __m128i,
614
            #[cfg(not(target_feature = "sse4.1"))]
615
            moddiv10: __m128i,
616
            zeros: __m128i,
617
        }
618
619
        static C: C = C {
620
            div10k: _mm_set1_epi64x(DIV10K_SIG as i64),
621
            divmod10k: _mm_set1_epi64x(NEG10K as i64),
622
            div100: _mm_set1_epi32(DIV100_SIG as i32),
623
            divmod100: _mm_set1_epi32(NEG100 as i32),
624
            div10: _mm_set1_epi16(((1i32 << 16) / 10 + 1) as i16),
625
            #[cfg(target_feature = "sse4.1")]
626
            neg10: _mm_set1_epi16((1 << 8) - 10),
627
            #[cfg(target_feature = "sse4.1")]
628
            bswap: _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
629
            #[cfg(not(target_feature = "sse4.1"))]
630
            hundred: _mm_set1_epi32(100),
631
            #[cfg(not(target_feature = "sse4.1"))]
632
            moddiv10: _mm_set1_epi16(10 * (1 << 8) - 1),
633
            zeros: _mm_set1_epi64x(ZEROS as i64),
634
        };
635
636
        // The BCD sequences are based on ones provided by Xiang JunBo.
637
        unsafe {
638
0
            let x: __m128i = _mm_set_epi64x(i64::from(bbccddee), i64::from(ffgghhii));
639
0
            let y: __m128i = _mm_add_epi64(
640
0
                x,
641
0
                _mm_mul_epu32(
642
0
                    C.divmod10k,
643
0
                    _mm_srli_epi64(_mm_mul_epu32(x, C.div10k), DIV10K_EXP),
644
                ),
645
            );
646
647
            #[cfg(target_feature = "sse4.1")]
648
            let bcd: __m128i = {
649
                // _mm_mullo_epi32 is SSE 4.1
650
                let z: __m128i = _mm_add_epi64(
651
                    y,
652
                    _mm_mullo_epi32(C.divmod100, _mm_srli_epi32(_mm_mulhi_epu16(y, C.div100), 3)),
653
                );
654
                let big_endian_bcd: __m128i =
655
                    _mm_add_epi64(z, _mm_mullo_epi16(C.neg10, _mm_mulhi_epu16(z, C.div10)));
656
                // SSSE3
657
                _mm_shuffle_epi8(big_endian_bcd, C.bswap)
658
            };
659
660
            #[cfg(not(target_feature = "sse4.1"))]
661
0
            let bcd: __m128i = {
662
0
                let y_div_100: __m128i = _mm_srli_epi16(_mm_mulhi_epu16(y, C.div100), 3);
663
0
                let y_mod_100: __m128i = _mm_sub_epi16(y, _mm_mullo_epi16(y_div_100, C.hundred));
664
0
                let z: __m128i = _mm_or_si128(_mm_slli_epi32(y_mod_100, 16), y_div_100);
665
0
                let bcd_shuffled: __m128i = _mm_sub_epi16(
666
0
                    _mm_slli_epi16(z, 8),
667
0
                    _mm_mullo_epi16(C.moddiv10, _mm_mulhi_epu16(z, C.div10)),
668
                );
669
0
                _mm_shuffle_epi32(bcd_shuffled, _MM_SHUFFLE(0, 1, 2, 3))
670
            };
671
672
0
            let digits = _mm_or_si128(bcd, C.zeros);
673
674
            // determine number of leading zeros
675
0
            let mask128: __m128i = _mm_cmpgt_epi8(bcd, _mm_setzero_si128());
676
0
            let mask = _mm_movemask_epi8(mask128) as u16;
677
0
            let len = 32 - u32::from(mask).leading_zeros();
678
679
0
            _mm_storeu_si128(buffer.cast::<__m128i>(), digits);
680
0
            buffer.add(len as usize)
681
        }
682
    }
683
684
    #[cfg(not(any(
685
        all(target_arch = "aarch64", target_feature = "neon", not(miri)),
686
        all(target_arch = "x86_64", target_feature = "sse2", not(miri)),
687
    )))]
688
    {
689
        // Each digits is denoted by a letter so value is abbccddeeffgghhii.
690
        let abbccddee = (value / 100_000_000) as u32;
691
        let ffgghhii = (value % 100_000_000) as u32;
692
        buffer = unsafe { write_if(buffer, abbccddee / 100_000_000, has17digits) };
693
        let bcd = to_bcd8(u64::from(abbccddee % 100_000_000));
694
        unsafe {
695
            write8(buffer, bcd | ZEROS);
696
        }
697
        if ffgghhii == 0 {
698
            return unsafe { buffer.add(count_trailing_nonzeros(bcd)) };
699
        }
700
        let bcd = to_bcd8(u64::from(ffgghhii));
701
        unsafe {
702
            write8(buffer.add(8), bcd | ZEROS);
703
            buffer.add(8).add(count_trailing_nonzeros(bcd))
704
        }
705
    }
706
0
}
707
708
// Writes a significand consisting of up to 9 decimal digits (8-9 for normals)
709
// and removes trailing zeros.
710
#[cfg_attr(feature = "no-panic", no_panic)]
711
0
unsafe fn write_significand9(mut buffer: *mut u8, value: u32, has9digits: bool) -> *mut u8 {
712
0
    buffer = unsafe { write_if(buffer, value / 100_000_000, has9digits) };
713
0
    let bcd = to_bcd8(u64::from(value % 100_000_000));
714
    unsafe {
715
0
        write8(buffer, bcd | ZEROS);
716
0
        buffer.add(count_trailing_nonzeros(bcd))
717
    }
718
0
}
719
720
0
fn normalize<UInt>(mut dec: dec_fp, subnormal: bool) -> dec_fp
721
0
where
722
0
    UInt: traits::UInt,
723
{
724
0
    if !subnormal {
725
0
        return dec;
726
0
    }
727
0
    let num_bits = mem::size_of::<UInt>() * 8;
728
0
    while dec.sig
729
0
        < if num_bits == 64 {
730
0
            10_000_000_000_000_000
731
        } else {
732
0
            100_000_000
733
        }
734
0
    {
735
0
        dec.sig *= 10;
736
0
        dec.exp -= 1;
737
0
    }
738
0
    dec
739
0
}
Unexecuted instantiation: zmij::normalize::<u32>
Unexecuted instantiation: zmij::normalize::<u64>
740
741
// Converts a binary FP number bin_sig * 2**bin_exp to the shortest decimal
742
// representation, where bin_exp = raw_exp - num_sig_bits - exp_bias.
743
#[cfg_attr(feature = "no-panic", no_panic)]
744
0
fn to_decimal<Float, UInt>(bin_sig: UInt, raw_exp: i64, regular: bool, subnormal: bool) -> dec_fp
745
0
where
746
0
    Float: FloatTraits,
747
0
    UInt: traits::UInt,
748
{
749
0
    let mut bin_exp = raw_exp - i64::from(Float::NUM_SIG_BITS) - i64::from(Float::EXP_BIAS);
750
0
    let num_bits = mem::size_of::<UInt>() as i32 * 8;
751
    // An optimization from yy by Yaoyuan Guo:
752
0
    while regular && !subnormal {
753
0
        let dec_exp = if USE_UMUL128_HI64 {
754
0
            umul128_hi64(bin_exp as u64, 0x4d10500000000000) as i32
755
        } else {
756
0
            compute_dec_exp(bin_exp as i32, true)
757
        };
758
0
        let exp_shift = unsafe { compute_exp_shift::<UInt, true>(bin_exp as i32, dec_exp) };
759
0
        let pow10 = unsafe { POW10_SIGNIFICANDS.get_unchecked(-dec_exp) };
760
761
        let integral; // integral part of bin_sig * pow10
762
        let fractional; // fractional part of bin_sig * pow10
763
0
        if num_bits == 64 {
764
0
            let p = umul192_hi128(pow10.hi, pow10.lo, (bin_sig << exp_shift).into());
765
0
            integral = UInt::truncate(p.hi);
766
0
            fractional = p.lo;
767
0
        } else {
768
0
            let p = umul128(pow10.hi, (bin_sig << exp_shift).into());
769
0
            integral = UInt::truncate((p >> 64) as u64);
770
0
            fractional = p as u64;
771
0
        }
772
        const HALF_ULP: u64 = 1 << 63;
773
774
        // Exact half-ulp tie when rounding to nearest integer.
775
0
        if fractional == HALF_ULP {
776
0
            break;
777
0
        }
778
779
        #[cfg(all(any(target_arch = "aarch64", target_arch = "x86_64"), not(miri)))]
780
0
        let digit = {
781
            // An optimization of integral % 10 by Dougall Johnson. Relies on
782
            // range calculation: (max_bin_sig << max_exp_shift) * max_u128.
783
0
            let quo10 = ((u128::from(integral.into()) * ((1 << 64) / 10 + 1)) >> 64) as u64;
784
0
            let mut digit = integral.into() - quo10 * 10;
785
0
            unsafe {
786
0
                asm!("/*{0}*/", inout(reg) digit); // or it narrows to 32-bit and doesn't use madd/msub
787
0
            }
788
0
            digit
789
        };
790
        #[cfg(not(all(any(target_arch = "aarch64", target_arch = "x86_64"), not(miri))))]
791
        let digit = integral.into() % 10;
792
793
        // Switch to a fixed-point representation with the least significant
794
        // integral digit in the upper bits and fractional digits in the lower
795
        // bits.
796
0
        let num_integral_bits = if num_bits == 64 { 4 } else { 32 };
797
0
        let num_fractional_bits = 64 - num_integral_bits;
798
0
        let ten = 10u64 << num_fractional_bits;
799
        // Fixed-point remainder of the scaled significand modulo 10.
800
0
        let scaled_sig_mod10 = (digit << num_fractional_bits) | (fractional >> num_integral_bits);
801
802
        // scaled_half_ulp = 0.5 * pow10 in the fixed-point format.
803
        // dec_exp is chosen so that 10**dec_exp <= 2**bin_exp < 10**(dec_exp + 1).
804
        // Since 1ulp == 2**bin_exp it will be in the range [1, 10) after scaling
805
        // by 10**dec_exp. Add 1 to combine the shift with division by two.
806
0
        let scaled_half_ulp = pow10.hi >> (num_integral_bits - exp_shift + 1);
807
0
        let upper = scaled_sig_mod10 + scaled_half_ulp;
808
809
        // value = 5.0507837461e-27
810
        // next  = 5.0507837461000010e-27
811
        //
812
        // c = integral.fractional' = 50507837461000003.153987... (value)
813
        //                            50507837461000010.328635... (next)
814
        //          scaled_half_ulp =                 3.587324...
815
        //
816
        // fractional' = fractional / 2**64, fractional = 2840565642863009226
817
        //
818
        //      50507837461000000       c               upper     50507837461000010
819
        //              s              l|   L             |               S
820
        // ───┬────┬────┼────┬────┬────┼*-──┼────┬────┬───*┬────┬────┬────┼-*--┬───
821
        //    8    9    0    1    2    3    4    5    6    7    8    9    0 |  1
822
        //            └─────────────────┼─────────────────┘                next
823
        //                             1ulp
824
        //
825
        // s - shorter underestimate, S - shorter overestimate
826
        // l - longer underestimate,  L - longer overestimate
827
828
        // Check for boundary case when rounding down to nearest 10 and
829
        // near-boundary case when rounding up to nearest 10.
830
0
        if scaled_sig_mod10 == scaled_half_ulp
831
            // Case where upper == ten is insufficient: 1.342178e+08f.
832
            // upper == ten || upper == ten - 1
833
0
            || ten.wrapping_sub(upper) <= 1
834
        {
835
0
            break;
836
0
        }
837
838
0
        let round_up = upper >= ten;
839
0
        let shorter = (integral.into() - digit + u64::from(round_up) * 10) as i64;
840
0
        let longer = (integral.into() + u64::from(fractional >= HALF_ULP)) as i64;
841
0
        let use_shorter = scaled_sig_mod10 <= scaled_half_ulp || round_up;
842
0
        return dec_fp {
843
0
            #[cfg(zmij_no_select_unpredictable)]
844
0
            sig: if use_shorter { shorter } else { longer },
845
0
            #[cfg(not(zmij_no_select_unpredictable))]
846
0
            sig: hint::select_unpredictable(use_shorter, shorter, longer),
847
0
            exp: dec_exp,
848
0
        };
849
    }
850
0
    bin_exp += i64::from(subnormal);
851
852
0
    let dec_exp = compute_dec_exp(bin_exp as i32, regular);
853
0
    let exp_shift = unsafe { compute_exp_shift::<UInt, false>(bin_exp as i32, dec_exp) };
854
0
    let mut pow10 = unsafe { POW10_SIGNIFICANDS.get_unchecked(-dec_exp) };
855
856
    // Fallback to Schubfach to guarantee correctness in boundary cases. This
857
    // requires switching to strict overestimates of powers of 10.
858
0
    if num_bits == 64 {
859
0
        pow10.lo += 1;
860
0
    } else {
861
0
        pow10.hi += 1;
862
0
    }
863
864
    // Shift the significand so that boundaries are integer.
865
    const BOUND_SHIFT: u32 = 2;
866
0
    let bin_sig_shifted = bin_sig << BOUND_SHIFT;
867
868
    // Compute the estimates of lower and upper bounds of the rounding interval
869
    // by multiplying them by the power of 10 and applying modified rounding.
870
0
    let lsb = bin_sig & UInt::from(1);
871
0
    let lower = (bin_sig_shifted - (UInt::from(regular) + UInt::from(1))) << exp_shift;
872
0
    let lower = umulhi_inexact_to_odd(pow10.hi, pow10.lo, lower) + lsb;
873
0
    let upper = (bin_sig_shifted + UInt::from(2)) << exp_shift;
874
0
    let upper = umulhi_inexact_to_odd(pow10.hi, pow10.lo, upper) - lsb;
875
876
    // The idea of using a single shorter candidate is by Cassio Neri.
877
    // It is less or equal to the upper bound by construction.
878
0
    let shorter = UInt::from(10) * ((upper >> BOUND_SHIFT) / UInt::from(10));
879
0
    if (shorter << BOUND_SHIFT) >= lower {
880
0
        return normalize::<UInt>(
881
0
            dec_fp {
882
0
                sig: shorter.into() as i64,
883
0
                exp: dec_exp,
884
0
            },
885
0
            subnormal,
886
        );
887
0
    }
888
889
0
    let scaled_sig = umulhi_inexact_to_odd(pow10.hi, pow10.lo, bin_sig_shifted << exp_shift);
890
0
    let longer_below = scaled_sig >> BOUND_SHIFT;
891
0
    let longer_above = longer_below + UInt::from(1);
892
893
    // Pick the closest of longer_below and longer_above and check if it's in
894
    // the rounding interval.
895
0
    let cmp = scaled_sig
896
0
        .wrapping_sub((longer_below + longer_above) << 1)
897
0
        .to_signed();
898
0
    let below_closer = cmp < UInt::from(0).to_signed()
899
0
        || (cmp == UInt::from(0).to_signed() && (longer_below & UInt::from(1)) == UInt::from(0));
900
0
    let below_in = (longer_below << BOUND_SHIFT) >= lower;
901
0
    let dec_sig = if below_closer & below_in {
902
0
        longer_below
903
    } else {
904
0
        longer_above
905
    };
906
0
    normalize::<UInt>(
907
0
        dec_fp {
908
0
            sig: dec_sig.into() as i64,
909
0
            exp: dec_exp,
910
0
        },
911
0
        subnormal,
912
    )
913
0
}
Unexecuted instantiation: zmij::to_decimal::<f64, u64>
Unexecuted instantiation: zmij::to_decimal::<f32, u32>
914
915
/// Writes the shortest correctly rounded decimal representation of `value` to
916
/// `buffer`. `buffer` should point to a buffer of size `buffer_size` or larger.
917
#[cfg_attr(feature = "no-panic", no_panic)]
918
0
unsafe fn write<Float>(value: Float, mut buffer: *mut u8) -> *mut u8
919
0
where
920
0
    Float: FloatTraits,
921
{
922
0
    let bits = value.to_bits();
923
    // It is beneficial to extract exponent and significand early.
924
0
    let bin_exp = Float::get_exp(bits); // binary exponent
925
0
    let mut bin_sig = Float::get_sig(bits); // binary significand
926
927
0
    unsafe {
928
0
        *buffer = b'-';
929
0
    }
930
0
    buffer = unsafe { buffer.add(usize::from(Float::is_negative(bits))) };
931
932
0
    let regular = bin_sig != Float::SigType::from(0);
933
0
    let subnormal = bin_exp == 0;
934
0
    if bin_exp == 0 {
935
0
        if bin_sig == Float::SigType::from(0) {
936
            return unsafe {
937
0
                *buffer = b'0';
938
0
                *buffer.add(1) = b'.';
939
0
                *buffer.add(2) = b'0';
940
0
                buffer.add(3)
941
            };
942
0
        }
943
0
        bin_sig |= Float::IMPLICIT_BIT;
944
0
    }
945
0
    bin_sig ^= Float::IMPLICIT_BIT;
946
947
    // Here be 🐉s.
948
0
    let mut dec = to_decimal::<Float, Float::SigType>(bin_sig, bin_exp, regular, subnormal);
949
0
    let mut dec_exp = dec.exp;
950
951
    // Write significand.
952
0
    let end = if Float::NUM_BITS == 64 {
953
0
        let has17digits = dec.sig >= 10_000_000_000_000_000;
954
0
        dec_exp += Float::MAX_DIGITS10 as i32 - 2 + i32::from(has17digits);
955
0
        unsafe { write_significand17(buffer.add(1), dec.sig as u64, has17digits) }
956
    } else {
957
0
        if dec.sig < 10_000_000 {
958
0
            dec.sig *= 10;
959
0
            dec_exp -= 1;
960
0
        }
961
0
        let has9digits = dec.sig >= 100_000_000;
962
0
        dec_exp += Float::MAX_DIGITS10 as i32 - 2 + i32::from(has9digits);
963
0
        unsafe { write_significand9(buffer.add(1), dec.sig as u32, has9digits) }
964
    };
965
966
0
    let length = unsafe { end.offset_from(buffer.add(1)) } as usize;
967
968
0
    if Float::NUM_BITS == 32 && (-6..=12).contains(&dec_exp)
969
0
        || Float::NUM_BITS == 64 && (-5..=15).contains(&dec_exp)
970
    {
971
0
        if length as i32 - 1 <= dec_exp {
972
            // 1234e7 -> 12340000000.0
973
            return unsafe {
974
0
                ptr::copy(buffer.add(1), buffer, length);
975
0
                ptr::write_bytes(buffer.add(length), b'0', dec_exp as usize + 3 - length);
976
0
                *buffer.add(dec_exp as usize + 1) = b'.';
977
0
                buffer.add(dec_exp as usize + 3)
978
            };
979
0
        } else if 0 <= dec_exp {
980
            // 1234e-2 -> 12.34
981
            return unsafe {
982
0
                ptr::copy(buffer.add(1), buffer, dec_exp as usize + 1);
983
0
                *buffer.add(dec_exp as usize + 1) = b'.';
984
0
                buffer.add(length + 1)
985
            };
986
        } else {
987
            // 1234e-6 -> 0.001234
988
            return unsafe {
989
0
                ptr::copy(buffer.add(1), buffer.add((1 - dec_exp) as usize), length);
990
0
                ptr::write_bytes(buffer, b'0', (1 - dec_exp) as usize);
991
0
                *buffer.add(1) = b'.';
992
0
                buffer.add((1 - dec_exp) as usize + length)
993
            };
994
        }
995
0
    }
996
997
0
    unsafe {
998
0
        // 1234e30 -> 1.234e33
999
0
        *buffer = *buffer.add(1);
1000
0
        *buffer.add(1) = b'.';
1001
0
    }
1002
0
    buffer = unsafe { buffer.add(length + usize::from(length > 1)) };
1003
1004
    // Write exponent.
1005
0
    let sign_ptr = buffer;
1006
0
    let e_sign = if dec_exp >= 0 {
1007
0
        (u16::from(b'+') << 8) | u16::from(b'e')
1008
    } else {
1009
0
        (u16::from(b'-') << 8) | u16::from(b'e')
1010
    };
1011
0
    buffer = unsafe { buffer.add(1) };
1012
0
    dec_exp = if dec_exp >= 0 { dec_exp } else { -dec_exp };
1013
0
    buffer = unsafe { buffer.add(usize::from(dec_exp >= 10)) };
1014
0
    if Float::MIN_10_EXP >= -99 && Float::MAX_10_EXP <= 99 {
1015
        unsafe {
1016
0
            buffer
1017
0
                .cast::<u16>()
1018
0
                .write_unaligned(*digits2(dec_exp as usize));
1019
0
            sign_ptr.cast::<u16>().write_unaligned(e_sign.to_le());
1020
0
            return buffer.add(2);
1021
        }
1022
0
    }
1023
1024
    // digit = dec_exp / 100
1025
0
    let digit = if USE_UMUL128_HI64 {
1026
0
        umul128_hi64(dec_exp as u64, 0x290000000000000) as u32
1027
    } else {
1028
0
        (dec_exp as u32 * DIV100_SIG) >> DIV100_EXP
1029
    };
1030
0
    unsafe {
1031
0
        *buffer = b'0' + digit as u8;
1032
0
    }
1033
0
    buffer = unsafe { buffer.add(usize::from(dec_exp >= 100)) };
1034
    unsafe {
1035
0
        buffer
1036
0
            .cast::<u16>()
1037
0
            .write_unaligned(*digits2((dec_exp as u32 - digit * 100) as usize));
1038
0
        sign_ptr.cast::<u16>().write_unaligned(e_sign.to_le());
1039
0
        buffer.add(2)
1040
    }
1041
0
}
Unexecuted instantiation: zmij::write::<f64>
Unexecuted instantiation: zmij::write::<f32>
1042
1043
/// Safe API for formatting floating point numbers to text.
1044
///
1045
/// ## Example
1046
///
1047
/// ```
1048
/// let mut buffer = zmij::Buffer::new();
1049
/// let printed = buffer.format_finite(1.234);
1050
/// assert_eq!(printed, "1.234");
1051
/// ```
1052
pub struct Buffer {
1053
    bytes: [MaybeUninit<u8>; BUFFER_SIZE],
1054
}
1055
1056
impl Buffer {
1057
    /// This is a cheap operation; you don't need to worry about reusing buffers
1058
    /// for efficiency.
1059
    #[inline]
1060
    #[cfg_attr(feature = "no-panic", no_panic)]
1061
0
    pub fn new() -> Self {
1062
0
        let bytes = [MaybeUninit::<u8>::uninit(); BUFFER_SIZE];
1063
0
        Buffer { bytes }
1064
0
    }
Unexecuted instantiation: <zmij::Buffer>::new
Unexecuted instantiation: <zmij::Buffer>::new
1065
1066
    /// Print a floating point number into this buffer and return a reference to
1067
    /// its string representation within the buffer.
1068
    ///
1069
    /// # Special cases
1070
    ///
1071
    /// This function formats NaN as the string "NaN", positive infinity as
1072
    /// "inf", and negative infinity as "-inf" to match std::fmt.
1073
    ///
1074
    /// If your input is known to be finite, you may get better performance by
1075
    /// calling the `format_finite` method instead of `format` to avoid the
1076
    /// checks for special cases.
1077
    #[cfg_attr(feature = "no-panic", no_panic)]
1078
0
    pub fn format<F: Float>(&mut self, f: F) -> &str {
1079
0
        if f.is_nonfinite() {
1080
0
            f.format_nonfinite()
1081
        } else {
1082
0
            self.format_finite(f)
1083
        }
1084
0
    }
Unexecuted instantiation: <zmij::Buffer>::format::<f64>
Unexecuted instantiation: <zmij::Buffer>::format::<_>
1085
1086
    /// Print a floating point number into this buffer and return a reference to
1087
    /// its string representation within the buffer.
1088
    ///
1089
    /// # Special cases
1090
    ///
1091
    /// This function **does not** check for NaN or infinity. If the input
1092
    /// number is not a finite float, the printed representation will be some
1093
    /// correctly formatted but unspecified numerical value.
1094
    ///
1095
    /// Please check [`is_finite`] yourself before calling this function, or
1096
    /// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
1097
    ///
1098
    /// [`is_finite`]: f64::is_finite
1099
    /// [`is_nan`]: f64::is_nan
1100
    /// [`is_infinite`]: f64::is_infinite
1101
    #[cfg_attr(feature = "no-panic", no_panic)]
1102
0
    pub fn format_finite<F: Float>(&mut self, f: F) -> &str {
1103
        unsafe {
1104
0
            let end = f.write_to_zmij_buffer(self.bytes.as_mut_ptr().cast::<u8>());
1105
0
            let len = end.offset_from(self.bytes.as_ptr().cast::<u8>()) as usize;
1106
0
            let slice = slice::from_raw_parts(self.bytes.as_ptr().cast::<u8>(), len);
1107
0
            str::from_utf8_unchecked(slice)
1108
        }
1109
0
    }
Unexecuted instantiation: <zmij::Buffer>::format_finite::<f64>
Unexecuted instantiation: <zmij::Buffer>::format_finite::<f32>
Unexecuted instantiation: <zmij::Buffer>::format_finite::<_>
1110
}
1111
1112
/// A floating point number, f32 or f64, that can be written into a
1113
/// [`zmij::Buffer`][Buffer].
1114
///
1115
/// This trait is sealed and cannot be implemented for types outside of the
1116
/// `zmij` crate.
1117
#[allow(unknown_lints)] // rustc older than 1.74
1118
#[allow(private_bounds)]
1119
pub trait Float: private::Sealed {}
1120
impl Float for f32 {}
1121
impl Float for f64 {}
1122
1123
mod private {
1124
    pub trait Sealed: crate::traits::Float {
1125
        fn is_nonfinite(self) -> bool;
1126
        fn format_nonfinite(self) -> &'static str;
1127
        unsafe fn write_to_zmij_buffer(self, buffer: *mut u8) -> *mut u8;
1128
    }
1129
1130
    impl Sealed for f32 {
1131
        #[inline]
1132
0
        fn is_nonfinite(self) -> bool {
1133
            const EXP_MASK: u32 = 0x7f800000;
1134
0
            let bits = self.to_bits();
1135
0
            bits & EXP_MASK == EXP_MASK
1136
0
        }
1137
1138
        #[cold]
1139
        #[cfg_attr(feature = "no-panic", inline)]
1140
0
        fn format_nonfinite(self) -> &'static str {
1141
            const MANTISSA_MASK: u32 = 0x007fffff;
1142
            const SIGN_MASK: u32 = 0x80000000;
1143
0
            let bits = self.to_bits();
1144
0
            if bits & MANTISSA_MASK != 0 {
1145
0
                crate::NAN
1146
0
            } else if bits & SIGN_MASK != 0 {
1147
0
                crate::NEG_INFINITY
1148
            } else {
1149
0
                crate::INFINITY
1150
            }
1151
0
        }
1152
1153
        #[cfg_attr(feature = "no-panic", inline)]
1154
0
        unsafe fn write_to_zmij_buffer(self, buffer: *mut u8) -> *mut u8 {
1155
0
            unsafe { crate::write(self, buffer) }
1156
0
        }
1157
    }
1158
1159
    impl Sealed for f64 {
1160
        #[inline]
1161
0
        fn is_nonfinite(self) -> bool {
1162
            const EXP_MASK: u64 = 0x7ff0000000000000;
1163
0
            let bits = self.to_bits();
1164
0
            bits & EXP_MASK == EXP_MASK
1165
0
        }
Unexecuted instantiation: <f64 as zmij::private::Sealed>::is_nonfinite
Unexecuted instantiation: <f64 as zmij::private::Sealed>::is_nonfinite
1166
1167
        #[cold]
1168
        #[cfg_attr(feature = "no-panic", inline)]
1169
0
        fn format_nonfinite(self) -> &'static str {
1170
            const MANTISSA_MASK: u64 = 0x000fffffffffffff;
1171
            const SIGN_MASK: u64 = 0x8000000000000000;
1172
0
            let bits = self.to_bits();
1173
0
            if bits & MANTISSA_MASK != 0 {
1174
0
                crate::NAN
1175
0
            } else if bits & SIGN_MASK != 0 {
1176
0
                crate::NEG_INFINITY
1177
            } else {
1178
0
                crate::INFINITY
1179
            }
1180
0
        }
1181
1182
        #[cfg_attr(feature = "no-panic", inline)]
1183
0
        unsafe fn write_to_zmij_buffer(self, buffer: *mut u8) -> *mut u8 {
1184
0
            unsafe { crate::write(self, buffer) }
1185
0
        }
1186
    }
1187
}
1188
1189
impl Default for Buffer {
1190
    #[inline]
1191
    #[cfg_attr(feature = "no-panic", no_panic)]
1192
0
    fn default() -> Self {
1193
0
        Buffer::new()
1194
0
    }
1195
}