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
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]
65mod diyfp;
66#[macro_use]
67mod dtoa;
68
69use core::mem::MaybeUninit;
70use core::slice;
71use core::str;
72#[cfg(feature = "no-panic")]
73use no_panic::no_panic;
74
75const NAN: &str = "NaN";
76const INFINITY: &str = "inf";
77const 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/// ```
89pub struct Buffer {
90    bytes: [MaybeUninit<u8>; 25],
91}
92
93impl Default for Buffer {
94    #[inline]
95    fn default() -> Buffer {
96        Buffer::new()
97    }
98}
99
100impl Copy for Buffer {}
101
102#[allow(clippy::non_canonical_clone_impl)]
103impl Clone for Buffer {
104    #[inline]
105    fn clone(&self) -> Self {
106        Buffer::new()
107    }
108}
109
110impl 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    pub fn new() -> Buffer {
116        let bytes = [MaybeUninit::<u8>::uninit(); 25];
117        Buffer { bytes }
118    }
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    pub fn format<F: Float>(&mut self, value: F) -> &str {
133        if value.is_nonfinite() {
134            value.format_nonfinite()
135        } else {
136            self.format_finite(value)
137        }
138    }
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    pub fn format_finite<F: Float>(&mut self, value: F) -> &str {
157        value.write(self)
158    }
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.
164pub trait Float: private::Sealed {}
165
166impl Float for f32 {}
167impl Float for f64 {}
168
169// Seal to prevent downstream implementations of Float trait.
170mod 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
178impl private::Sealed for f32 {
179    #[inline]
180    #[cfg_attr(feature = "no-panic", no_panic)]
181    fn is_nonfinite(self) -> bool {
182        const EXP_MASK: u32 = 0x7f800000;
183        let bits = self.to_bits();
184        bits & EXP_MASK == EXP_MASK
185    }
186
187    #[cold]
188    #[cfg_attr(feature = "no-panic", no_panic)]
189    fn format_nonfinite(self) -> &'static str {
190        const MANTISSA_MASK: u32 = 0x007fffff;
191        const SIGN_MASK: u32 = 0x80000000;
192        let bits = self.to_bits();
193        if bits & MANTISSA_MASK != 0 {
194            NAN
195        } else if bits & SIGN_MASK != 0 {
196            NEG_INFINITY
197        } else {
198            INFINITY
199        }
200    }
201
202    #[inline]
203    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        unsafe { dtoa(buf, self) }
221    }
222}
223
224impl private::Sealed for f64 {
225    #[inline]
226    #[cfg_attr(feature = "no-panic", no_panic)]
227    fn is_nonfinite(self) -> bool {
228        const EXP_MASK: u64 = 0x7ff0000000000000;
229        let bits = self.to_bits();
230        bits & EXP_MASK == EXP_MASK
231    }
232
233    #[cold]
234    #[cfg_attr(feature = "no-panic", no_panic)]
235    fn format_nonfinite(self) -> &'static str {
236        const MANTISSA_MASK: u64 = 0x000fffffffffffff;
237        const SIGN_MASK: u64 = 0x8000000000000000;
238        let bits = self.to_bits();
239        if bits & MANTISSA_MASK != 0 {
240            NAN
241        } else if bits & SIGN_MASK != 0 {
242            NEG_INFINITY
243        } else {
244            INFINITY
245        }
246    }
247
248    #[inline]
249    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        unsafe { dtoa(buf, self) }
267    }
268}
269
270////////////////////////////////////////////////////////////////////////////////
271
272const MAX_DECIMAL_PLACES: isize = 324;
273
274static 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]
283static 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]
290static 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]
296static 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]
344static 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];