Coverage Report

Created: 2026-03-11 07:34

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