/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 | | //!  |
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 | | ]; |