dtoa/
lib.rs

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]
58mod diyfp;
59#[macro_use]
60mod dtoa;
61
62use core::mem::{self, MaybeUninit};
63use core::slice;
64use core::str;
65#[cfg(feature = "no-panic")]
66use no_panic::no_panic;
67
68const NAN: &str = "NaN";
69const INFINITY: &str = "inf";
70const 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/// ```
82pub struct Buffer {
83    bytes: [MaybeUninit<u8>; 25],
84}
85
86impl Default for Buffer {
87    #[inline]
88    fn default() -> Buffer {
89        Buffer::new()
90    }
91}
92
93impl Copy for Buffer {}
94
95impl Clone for Buffer {
96    #[inline]
97    #[allow(clippy::non_canonical_clone_impl)] // false positive https://github.com/rust-lang/rust-clippy/issues/11072
98    fn clone(&self) -> Self {
99        Buffer::new()
100    }
101}
102
103impl 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    pub fn new() -> Buffer {
109        let bytes = [MaybeUninit::<u8>::uninit(); 25];
110        Buffer { bytes }
111    }
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    pub fn format<F: Float>(&mut self, value: F) -> &str {
126        if value.is_nonfinite() {
127            value.format_nonfinite()
128        } else {
129            self.format_finite(value)
130        }
131    }
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    pub fn format_finite<F: Float>(&mut self, value: F) -> &str {
150        value.write(self)
151    }
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.
157pub trait Float: private::Sealed {}
158
159impl Float for f32 {}
160impl Float for f64 {}
161
162// Seal to prevent downstream implementations of Float trait.
163mod 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
171impl private::Sealed for f32 {
172    #[inline]
173    #[cfg_attr(feature = "no-panic", no_panic)]
174    fn is_nonfinite(self) -> bool {
175        const EXP_MASK: u32 = 0x7f800000;
176        let bits = self.to_bits();
177        bits & EXP_MASK == EXP_MASK
178    }
179
180    #[cold]
181    #[cfg_attr(feature = "no-panic", no_panic)]
182    fn format_nonfinite(self) -> &'static str {
183        const MANTISSA_MASK: u32 = 0x007fffff;
184        const SIGN_MASK: u32 = 0x80000000;
185        let bits = self.to_bits();
186        if bits & MANTISSA_MASK != 0 {
187            NAN
188        } else if bits & SIGN_MASK != 0 {
189            NEG_INFINITY
190        } else {
191            INFINITY
192        }
193    }
194
195    #[inline]
196    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        unsafe { dtoa(buf, self) }
214    }
215}
216
217impl private::Sealed for f64 {
218    #[inline]
219    #[cfg_attr(feature = "no-panic", no_panic)]
220    fn is_nonfinite(self) -> bool {
221        const EXP_MASK: u64 = 0x7ff0000000000000;
222        let bits = self.to_bits();
223        bits & EXP_MASK == EXP_MASK
224    }
225
226    #[cold]
227    #[cfg_attr(feature = "no-panic", no_panic)]
228    fn format_nonfinite(self) -> &'static str {
229        const MANTISSA_MASK: u64 = 0x000fffffffffffff;
230        const SIGN_MASK: u64 = 0x8000000000000000;
231        let bits = self.to_bits();
232        if bits & MANTISSA_MASK != 0 {
233            NAN
234        } else if bits & SIGN_MASK != 0 {
235            NEG_INFINITY
236        } else {
237            INFINITY
238        }
239    }
240
241    #[inline]
242    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        unsafe { dtoa(buf, self) }
260    }
261}
262
263////////////////////////////////////////////////////////////////////////////////
264
265const MAX_DECIMAL_PLACES: isize = 324;
266
267static 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]
276static 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]
283static 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]
289static 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]
337static 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];