Coverage Report

Created: 2026-03-28 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/dtoa-1.0.11/src/lib.rs
Line
Count
Source
1
//! [![github]](https://github.com/dtolnay/dtoa) [![crates-io]](https://crates.io/crates/dtoa) [![docs-rs]](https://docs.rs/dtoa)
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
//! This crate provides fast conversion of floating point primitives to decimal
10
//! strings. The implementation is a straightforward Rust port of [Milo Yip]'s
11
//! C++ implementation [dtoa.h]. The original C++ code of each function is
12
//! included in comments.
13
//!
14
//! See also [`itoa`] for printing integer primitives.
15
//!
16
//! [Milo Yip]: https://github.com/miloyip
17
//! [dtoa.h]: https://github.com/miloyip/rapidjson/blob/master/include/rapidjson/internal/dtoa.h
18
//! [`itoa`]: https://github.com/dtolnay/itoa
19
//!
20
//! # Example
21
//!
22
//! ```
23
//! fn main() {
24
//!     let mut buffer = dtoa::Buffer::new();
25
//!     let printed = buffer.format(2.71828f64);
26
//!     assert_eq!(printed, "2.71828");
27
//! }
28
//! ```
29
//!
30
//! ## Performance
31
//!
32
//! The [dtoa-benchmark] compares this library and other Rust floating point
33
//! formatting implementations across a range of precisions. The vertical axis
34
//! in this chart shows nanoseconds taken by a single execution of
35
//! `dtoa::Buffer::new().format_finite(value)` so a lower result indicates a
36
//! faster library.
37
//!
38
//! [dtoa-benchmark]: https://github.com/dtolnay/dtoa-benchmark
39
//!
40
//! ![performance](https://raw.githubusercontent.com/dtolnay/dtoa/master/dtoa-benchmark.png)
41
42
#![doc(html_root_url = "https://docs.rs/dtoa/1.0.11")]
43
#![no_std]
44
#![allow(
45
    clippy::cast_lossless,
46
    clippy::cast_possible_truncation,
47
    clippy::cast_possible_wrap,
48
    clippy::cast_precision_loss,
49
    clippy::cast_sign_loss,
50
    clippy::doc_markdown,
51
    clippy::expl_impl_clone_on_copy,
52
    clippy::if_not_else,
53
    clippy::missing_errors_doc,
54
    clippy::must_use_candidate,
55
    clippy::needless_doctest_main,
56
    clippy::range_plus_one,
57
    clippy::semicolon_if_nothing_returned, // https://github.com/rust-lang/rust-clippy/issues/7768
58
    clippy::shadow_unrelated,
59
    clippy::suspicious_else_formatting,
60
    clippy::unreadable_literal,
61
    clippy::unseparated_literal_suffix
62
)]
63
64
#[macro_use]
65
mod diyfp;
66
#[macro_use]
67
mod dtoa;
68
69
use core::mem::MaybeUninit;
70
use core::slice;
71
use core::str;
72
#[cfg(feature = "no-panic")]
73
use no_panic::no_panic;
74
75
const NAN: &str = "NaN";
76
const INFINITY: &str = "inf";
77
const NEG_INFINITY: &str = "-inf";
78
79
/// A correctly sized stack allocation for the formatted float to be written
80
/// into.
81
///
82
/// # Example
83
///
84
/// ```
85
/// let mut buffer = dtoa::Buffer::new();
86
/// let printed = buffer.format_finite(2.71828);
87
/// assert_eq!(printed, "2.71828");
88
/// ```
89
pub struct Buffer {
90
    bytes: [MaybeUninit<u8>; 25],
91
}
92
93
impl Default for Buffer {
94
    #[inline]
95
0
    fn default() -> Buffer {
96
0
        Buffer::new()
97
0
    }
98
}
99
100
impl Copy for Buffer {}
101
102
#[allow(clippy::non_canonical_clone_impl)]
103
impl Clone for Buffer {
104
    #[inline]
105
0
    fn clone(&self) -> Self {
106
0
        Buffer::new()
107
0
    }
108
}
109
110
impl Buffer {
111
    /// This is a cheap operation; you don't need to worry about reusing buffers
112
    /// for efficiency.
113
    #[inline]
114
    #[cfg_attr(feature = "no-panic", no_panic)]
115
0
    pub fn new() -> Buffer {
116
0
        let bytes = [MaybeUninit::<u8>::uninit(); 25];
117
0
        Buffer { bytes }
118
0
    }
Unexecuted instantiation: <dtoa::Buffer>::new
Unexecuted instantiation: <dtoa::Buffer>::new
119
120
    /// Print a floating point number into this buffer and return a reference to
121
    /// its string representation within the buffer.
122
    ///
123
    /// # Special cases
124
    ///
125
    /// This function formats NaN as the string "NaN", positive infinity as
126
    /// "inf", and negative infinity as "-inf" to match std::fmt.
127
    ///
128
    /// If your input is known to be finite, you may get better performance by
129
    /// calling the `format_finite` method instead of `format` to avoid the
130
    /// checks for special cases.
131
    #[cfg_attr(feature = "no-panic", no_panic)]
132
0
    pub fn format<F: Float>(&mut self, value: F) -> &str {
133
0
        if value.is_nonfinite() {
134
0
            value.format_nonfinite()
135
        } else {
136
0
            self.format_finite(value)
137
        }
138
0
    }
Unexecuted instantiation: <dtoa::Buffer>::format::<f64>
Unexecuted instantiation: <dtoa::Buffer>::format::<_>
139
140
    /// Print a floating point number into this buffer and return a reference to
141
    /// its string representation within the buffer.
142
    ///
143
    /// # Special cases
144
    ///
145
    /// This function **does not** check for NaN or infinity. If the input
146
    /// number is not a finite float, the printed representation will be some
147
    /// correctly formatted but unspecified numerical value.
148
    ///
149
    /// Please check [`is_finite`] yourself before calling this function, or
150
    /// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
151
    ///
152
    /// [`is_finite`]: f64::is_finite
153
    /// [`is_nan`]: f64::is_nan
154
    /// [`is_infinite`]: f64::is_infinite
155
    #[cfg_attr(feature = "no-panic", no_panic)]
156
0
    pub fn format_finite<F: Float>(&mut self, value: F) -> &str {
157
0
        value.write(self)
158
0
    }
Unexecuted instantiation: <dtoa::Buffer>::format_finite::<f64>
Unexecuted instantiation: <dtoa::Buffer>::format_finite::<_>
159
}
160
161
/// A floating point number that can be written into a [`dtoa::Buffer`][Buffer].
162
///
163
/// This trait is sealed and cannot be implemented for types outside of dtoa.
164
pub trait Float: private::Sealed {}
165
166
impl Float for f32 {}
167
impl Float for f64 {}
168
169
// Seal to prevent downstream implementations of Float trait.
170
mod private {
171
    pub trait Sealed: Copy {
172
        fn is_nonfinite(self) -> bool;
173
        fn format_nonfinite(self) -> &'static str;
174
        fn write(self, buf: &mut crate::Buffer) -> &str;
175
    }
176
}
177
178
impl private::Sealed for f32 {
179
    #[inline]
180
    #[cfg_attr(feature = "no-panic", no_panic)]
181
0
    fn is_nonfinite(self) -> bool {
182
        const EXP_MASK: u32 = 0x7f800000;
183
0
        let bits = self.to_bits();
184
0
        bits & EXP_MASK == EXP_MASK
185
0
    }
186
187
    #[cold]
188
    #[cfg_attr(feature = "no-panic", no_panic)]
189
0
    fn format_nonfinite(self) -> &'static str {
190
        const MANTISSA_MASK: u32 = 0x007fffff;
191
        const SIGN_MASK: u32 = 0x80000000;
192
0
        let bits = self.to_bits();
193
0
        if bits & MANTISSA_MASK != 0 {
194
0
            NAN
195
0
        } else if bits & SIGN_MASK != 0 {
196
0
            NEG_INFINITY
197
        } else {
198
0
            INFINITY
199
        }
200
0
    }
201
202
    #[inline]
203
0
    fn write(self, buf: &mut Buffer) -> &str {
204
        dtoa! {
205
            floating_type: f32,
206
            significand_type: u32,
207
            exponent_type: i32,
208
209
            diy_significand_size: 32,
210
            significand_size: 23,
211
            exponent_bias: 0x7F,
212
            mask_type: u32,
213
            exponent_mask: 0x7F800000,
214
            significand_mask: 0x007FFFFF,
215
            hidden_bit: 0x00800000,
216
            cached_powers_f: CACHED_POWERS_F_32,
217
            cached_powers_e: CACHED_POWERS_E_32,
218
            min_power: (-36),
219
        };
220
0
        unsafe { dtoa(buf, self) }
221
0
    }
222
}
223
224
impl private::Sealed for f64 {
225
    #[inline]
226
    #[cfg_attr(feature = "no-panic", no_panic)]
227
0
    fn is_nonfinite(self) -> bool {
228
        const EXP_MASK: u64 = 0x7ff0000000000000;
229
0
        let bits = self.to_bits();
230
0
        bits & EXP_MASK == EXP_MASK
231
0
    }
Unexecuted instantiation: <f64 as dtoa::private::Sealed>::is_nonfinite
Unexecuted instantiation: <f64 as dtoa::private::Sealed>::is_nonfinite
232
233
    #[cold]
234
    #[cfg_attr(feature = "no-panic", no_panic)]
235
0
    fn format_nonfinite(self) -> &'static str {
236
        const MANTISSA_MASK: u64 = 0x000fffffffffffff;
237
        const SIGN_MASK: u64 = 0x8000000000000000;
238
0
        let bits = self.to_bits();
239
0
        if bits & MANTISSA_MASK != 0 {
240
0
            NAN
241
0
        } else if bits & SIGN_MASK != 0 {
242
0
            NEG_INFINITY
243
        } else {
244
0
            INFINITY
245
        }
246
0
    }
247
248
    #[inline]
249
0
    fn write(self, buf: &mut Buffer) -> &str {
250
        dtoa! {
251
            floating_type: f64,
252
            significand_type: u64,
253
            exponent_type: isize,
254
255
            diy_significand_size: 64,
256
            significand_size: 52,
257
            exponent_bias: 0x3FF,
258
            mask_type: u64,
259
            exponent_mask: 0x7FF0000000000000,
260
            significand_mask: 0x000FFFFFFFFFFFFF,
261
            hidden_bit: 0x0010000000000000,
262
            cached_powers_f: CACHED_POWERS_F_64,
263
            cached_powers_e: CACHED_POWERS_E_64,
264
            min_power: (-348),
265
        };
266
0
        unsafe { dtoa(buf, self) }
267
0
    }
Unexecuted instantiation: <f64 as dtoa::private::Sealed>::write
Unexecuted instantiation: <f64 as dtoa::private::Sealed>::write
268
}
269
270
////////////////////////////////////////////////////////////////////////////////
271
272
const MAX_DECIMAL_PLACES: isize = 324;
273
274
static DEC_DIGITS_LUT: [u8; 200] = *b"\
275
    0001020304050607080910111213141516171819\
276
    2021222324252627282930313233343536373839\
277
    4041424344454647484950515253545556575859\
278
    6061626364656667686970717273747576777879\
279
    8081828384858687888990919293949596979899";
280
281
// 10^-36, 10^-28, ..., 10^52
282
#[rustfmt::skip]
283
static CACHED_POWERS_F_32: [u32; 12] = [
284
    0xaa242499, 0xfd87b5f3, 0xbce50865, 0x8cbccc09,
285
    0xd1b71759, 0x9c400000, 0xe8d4a510, 0xad78ebc6,
286
    0x813f3979, 0xc097ce7c, 0x8f7e32ce, 0xd5d238a5,
287
];
288
289
#[rustfmt::skip]
290
static CACHED_POWERS_E_32: [i16; 12] = [
291
    -151, -125, -98, -71, -45, -18, 8, 35, 62, 88, 115, 141,
292
];
293
294
// 10^-348, 10^-340, ..., 10^340
295
#[rustfmt::skip]
296
static CACHED_POWERS_F_64: [u64; 87] = [
297
    0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76,
298
    0x8b16fb203055ac76, 0xcf42894a5dce35ea,
299
    0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
300
    0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f,
301
    0xbe5691ef416bd60c, 0x8dd01fad907ffc3c,
302
    0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
303
    0xea9c227723ee8bcb, 0xaecc49914078536d,
304
    0x823c12795db6ce57, 0xc21094364dfb5637,
305
    0x9096ea6f3848984f, 0xd77485cb25823ac7,
306
    0xa086cfcd97bf97f4, 0xef340a98172aace5,
307
    0xb23867fb2a35b28e, 0x84c8d4dfd2c63f3b,
308
    0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
309
    0xdbac6c247d62a584, 0xa3ab66580d5fdaf6,
310
    0xf3e2f893dec3f126, 0xb5b5ada8aaff80b8,
311
    0x87625f056c7c4a8b, 0xc9bcff6034c13053,
312
    0x964e858c91ba2655, 0xdff9772470297ebd,
313
    0xa6dfbd9fb8e5b88f, 0xf8a95fcf88747d94,
314
    0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
315
    0xcdb02555653131b6, 0x993fe2c6d07b7fac,
316
    0xe45c10c42a2b3b06, 0xaa242499697392d3,
317
    0xfd87b5f28300ca0e, 0xbce5086492111aeb,
318
    0x8cbccc096f5088cc, 0xd1b71758e219652c,
319
    0x9c40000000000000, 0xe8d4a51000000000,
320
    0xad78ebc5ac620000, 0x813f3978f8940984,
321
    0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70,
322
    0xd5d238a4abe98068, 0x9f4f2726179a2245,
323
    0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
324
    0x83c7088e1aab65db, 0xc45d1df942711d9a,
325
    0x924d692ca61be758, 0xda01ee641a708dea,
326
    0xa26da3999aef774a, 0xf209787bb47d6b85,
327
    0xb454e4a179dd1877, 0x865b86925b9bc5c2,
328
    0xc83553c5c8965d3d, 0x952ab45cfa97a0b3,
329
    0xde469fbd99a05fe3, 0xa59bc234db398c25,
330
    0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece,
331
    0x88fcf317f22241e2, 0xcc20ce9bd35c78a5,
332
    0x98165af37b2153df, 0xe2a0b5dc971f303a,
333
    0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c,
334
    0xbb764c4ca7a44410, 0x8bab8eefb6409c1a,
335
    0xd01fef10a657842c, 0x9b10a4e5e9913129,
336
    0xe7109bfba19c0c9d, 0xac2820d9623bf429,
337
    0x80444b5e7aa7cf85, 0xbf21e44003acdd2d,
338
    0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
339
    0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9,
340
    0xaf87023b9bf0ee6b,
341
];
342
343
#[rustfmt::skip]
344
static CACHED_POWERS_E_64: [i16; 87] = [
345
    -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007,  -980,
346
    -954,   -927,  -901,  -874,  -847,  -821,  -794,  -768,  -741,  -715,
347
    -688,   -661,  -635,  -608,  -582,  -555,  -529,  -502,  -475,  -449,
348
    -422,   -396,  -369,  -343,  -316,  -289,  -263,  -236,  -210,  -183,
349
    -157,   -130,  -103,   -77,   -50,   -24,     3,    30,    56,    83,
350
     109,    136,   162,   189,   216,   242,   269,   295,   322,   348,
351
     375,    402,   428,   455,   481,   508,   534,   561,   588,   614,
352
     641,    667,   694,   720,   747,   774,   800,   827,   853,   880,
353
     907,    933,   960,   986,  1013,  1039,  1066,
354
];