lexical_write_float/
lib.rs

1//! Fast and compact float-to-string conversions.
2//!
3//! This contains high-performance methods to write floating-point numbers
4//! directly to bytes, can be converted to [`str`] using
5//! [`str::from_utf8`]. Using [`to_lexical`] is analogous to [`to_string`],
6//! just writing to an existing buffer.
7//!
8//! It also contains extensively formatting control, including the use of
9//! exponent notation, if to round or truncate floats, the number of significant
10//! digits, and more.
11//!
12//! [`str::from_utf8`]: core::str::from_utf8
13//! [`to_lexical`]: ToLexical::to_lexical
14//!
15//! # Getting Started
16//!
17//! To write a number to bytes, use [`to_lexical`]:
18//!
19//! [`to_lexical`]: ToLexical::to_lexical
20//!
21//! ```rust
22//! # #[no_std]
23//! # use core::str;
24//! use lexical_write_float::{FormattedSize, ToLexical};
25//!
26//! let mut buffer = [0u8; f64::FORMATTED_SIZE_DECIMAL];
27//! let digits = 1.234f64.to_lexical(&mut buffer);
28//! assert_eq!(str::from_utf8(digits), Ok("1.234"));
29//! ```
30//!
31//! With the default options, using [`FORMATTED_SIZE_DECIMAL`]
32//! guarantees the buffer will be large enough to write the digits for all
33//! numbers of that type.
34//!
35//! [`FORMATTED_SIZE_DECIMAL`]: FormattedSize::FORMATTED_SIZE_DECIMAL
36//!
37//! # Options/Formatting API
38//!
39//! Each float formatter contains extensive formatting control, including
40//! a maximum number of significant digits written, a minimum number of
41//! significant digits remaining, the positive and negative exponent break
42//! points (at what exponent, in scientific-notation, to force scientific
43//! notation), whether to force or disable scientific notation, the rounding
44//! mode for truncated float strings, and how to display non-finite floats.
45//! While using custom float options, you must use
46//! [`Options::buffer_size_const`] to determine the correct buffer size:
47//!
48//! ```rust
49//! # #[cfg(feature = "format")] {
50//! # use core::str;
51//! use lexical_write_float::{format, options, ToLexicalWithOptions};
52//!
53//! const BUFFER_SIZE: usize = options::RUST_LITERAL
54//!     .buffer_size_const::<f64, { format::RUST_LITERAL }>();
55//!
56//! fn write_rust_float(f: f64) -> ([u8; BUFFER_SIZE], usize) {
57//!     let mut buffer = [0u8; BUFFER_SIZE];
58//!     let digits = f.to_lexical_with_options::<{ format::RUST_LITERAL }>(
59//!         &mut buffer,
60//!         &options::RUST_LITERAL
61//!     );
62//!     let count = digits.len();
63//!     (buffer, count)
64//! }
65//!
66//! let (digits, count) = write_rust_float(3.5);
67//! assert_eq!(str::from_utf8(&digits[..count]), Ok("3.5"));
68//! # }
69//! ```
70//!
71//! For additional supported options for customizing how to write floats, see
72//! the [`OptionsBuilder`]. If you're looking to parse floats with a grammar
73//! for a programming language, many pre-defined options such as for [`JSON`]
74//! exist in [`mod@options`]. For even more customization, see the
75//! [`format`](#format) and [Comprehensive Configuration] sections
76//! below.
77//!
78//! [`JSON`]: crate::options::JSON
79//! [Comprehensive Configuration]: #comprehensive-configuration
80//!
81//! # Features
82//!
83//! * `format` - Add support for custom float formatting.
84//! * `power-of-two` - Add support for writing power-of-two float strings.
85//! * `radix` - Add support for strings of any radix.
86//! * `compact` - Reduce code size at the cost of performance.
87//! * `f16` - Enable support for half-precision [`f16`][`ieee-f16`] and
88//!   [`bf16`][`brain-float`] floats.
89//! * `std` (Default) - Disable to allow use in a [`no_std`] environment.
90//!
91//! [`no_std`]: https://docs.rust-embedded.org/book/intro/no-std.html
92//! [`ieee-f16`]: https://en.wikipedia.org/wiki/Half-precision_floating-point_format
93//! [`brain-float`]: https://en.wikipedia.org/wiki/Bfloat16_floating-point_format
94//!
95//! A complete description of supported features includes:
96//!
97//! #### format
98//!
99//! Add support custom float formatting specifications. This should be used in
100//! conjunction with [`Options`] for extensible float writing. You must use
101//! [`Options::buffer_size_const`] to determine the number of bytes requires in
102//! the buffer. This allows changing the use of exponent notation, requiring or
103//! not allowing signs, and more.
104//!
105//! ##### JSON
106//!
107//! For example, in JSON, the following floats are valid or invalid:
108//!
109//! ```text
110//! -1          // valid
111//! +1          // invalid
112//! 1           // valid
113//! 1.          // invalid
114//! .1          // invalid
115//! 0.1         // valid
116//! nan         // invalid
117//! inf         // invalid
118//! Infinity    // invalid
119//! ```
120//!
121//! All of the finite numbers are valid in Rust, and Rust supports non-finite
122//! floats. In order to write standard-conforming JSON floats using
123//! `lexical-core`, you may use the following approach:
124//!
125//! ```rust
126//! # #[cfg(feature = "format")] {
127//! # use core::str;
128//! use lexical_write_float::{format, options, ToLexicalWithOptions};
129//!
130//! const BUFFER_SIZE: usize = options::JSON.buffer_size_const::<f64, { format::JSON }>();
131//!
132//! fn write_json_float(f: f64) -> ([u8; BUFFER_SIZE], usize) {
133//!     let mut buffer = [0u8; BUFFER_SIZE];
134//!     let digits = f.to_lexical_with_options::<{ format::JSON }>(
135//!         &mut buffer,
136//!         &options::JSON
137//!     );
138//!     let count = digits.len();
139//!     (buffer, count)
140//! }
141//!
142//! let (digits, count) = write_json_float(3.5);
143//! assert_eq!(str::from_utf8(&digits[..count]), Ok("3.5"));
144//! # }
145//! ```
146//!
147//! ##### Custom Signs
148//!
149//! An example of building a custom format to ensure positive signs are always
150//! written is as follows:
151//!
152//! ```rust
153//! # #[cfg(feature = "radix")] {
154//! # use core::str;
155//! use lexical_write_float::{FormattedSize, NumberFormatBuilder, Options, ToLexicalWithOptions};
156//!
157//! const FORMAT: u128 = NumberFormatBuilder::new()
158//!     // require a `+` or `-` sign before the number
159//!     .required_mantissa_sign(true)
160//!     // require a `+` or `-` sign before the exponent digits
161//!     .required_exponent_sign(true)
162//!     // build the format, panicking if the format is invalid
163//!     .build_strict();
164//! const OPTIONS: Options = Options::new();
165//!
166//! const BUFFER_SIZE: usize = OPTIONS.buffer_size_const::<f64, FORMAT>();
167//! let mut buffer = [0u8; BUFFER_SIZE];
168//!
169//! let digits = 1.234e300f64.to_lexical_with_options::<FORMAT>(&mut buffer, &OPTIONS);
170//! assert_eq!(str::from_utf8(digits), Ok("+1.234e+300"));
171//! # }
172//! ```
173//!
174//! Enabling the [`format`](crate#format) API significantly increases compile
175//! times, however, it enables a large amount of customization in how floats are
176//! written.
177//!
178//! #### power-of-two
179//!
180//! Enable writing numbers that are powers of two, that is, `2`, `4`, `8`, `16`,
181//! and `32`. In these cases, you should use [`FORMATTED_SIZE`] to create a
182//! sufficiently large buffer.
183//!
184//! [`FORMATTED_SIZE`]: FormattedSize::FORMATTED_SIZE
185//!
186//! ```rust
187//! # #[cfg(feature = "power-of-two")] {
188//! # use core::str;
189//! use lexical_write_float::{FormattedSize, NumberFormatBuilder, Options, ToLexicalWithOptions};
190//!
191//! let mut buffer = [0u8; f64::FORMATTED_SIZE];
192//! const BINARY: u128 = NumberFormatBuilder::binary();
193//! const OPTIONS: Options = Options::new();
194//! let digits = 1.234f64.to_lexical_with_options::<BINARY>(&mut buffer, &OPTIONS);
195//! assert_eq!(str::from_utf8(digits), Ok("1.0011101111100111011011001000101101000011100101011"));
196//! # }
197//! ```
198//!
199//! #### radix
200//!
201//! Enable writing numbers using all radixes from `2` to `36`. This requires
202//! more static storage than [`power-of-two`][crate#power-of-two], and increases
203//! compile times, but can be quite useful for esoteric programming languages
204//! which use duodecimal floats, for example.
205//!
206//! ```rust
207//! # #[cfg(feature = "radix")] {
208//! # use core::str;
209//! use lexical_write_float::{FormattedSize, NumberFormatBuilder, Options, ToLexicalWithOptions};
210//!
211//! const FORMAT: u128 = NumberFormatBuilder::from_radix(12);
212//! const OPTIONS: Options = Options::new();
213//!
214//! let mut buffer = [0u8; f64::FORMATTED_SIZE];
215//! let digits = 1.234f64.to_lexical_with_options::<FORMAT>(&mut buffer, &OPTIONS);
216//! assert_eq!(str::from_utf8(digits), Ok("1.29842830A44BAA2"));
217//! # }
218//! ```
219//!
220//! #### compact
221//!
222//! Reduce the generated code size at the cost of performance. This minimizes
223//! the number of static tables, inlining, and generics used, drastically
224//! reducing the size of the generated binaries. However, this resulting
225//! performance of the generated code is much lower.
226//!
227//! #### f16
228//!
229//! This enables the use of the half-precision floats [`f16`][`ieee-f16`] and
230//! [`bf16`][`brain-float`]. However, since these have limited hardware support
231//! and are primarily used for vectorized operations, they are formatted as if
232//! they were an [`f32`]. Due to the low precision of 16-bit floats, the results
233//! may appear to have significant rounding error.
234//!
235//! ```rust
236//! # #[cfg(feature = "f16")] {
237//! # use core::str;
238//! use lexical_write_float::{f16, FormattedSize, ToLexical};
239//!
240//! let mut buffer = [0u8; f16::FORMATTED_SIZE];
241//! let value = f16::from_f64_const(1.234f64);
242//! let digits = value.to_lexical(&mut buffer);
243//! assert_eq!(str::from_utf8(digits), Ok("1.234375"));
244//! # }
245//! ```
246//!
247//! #### std
248//!
249//! Enable use of the standard library. Currently, the standard library
250//! is not used, and may be disabled without any change in functionality
251//! on stable.
252//!
253//! # Comprehensive Configuration
254//!
255//! `lexical-write-float` provides two main levels of configuration:
256//! - The [`NumberFormatBuilder`], creating a packed struct with custom
257//!   formatting options.
258//! - The [`Options`] API.
259//!
260//! ## Number Format
261//!
262//! The number format class provides numerous flags to specify number writing.
263//! When the [`power-of-two`](#power-of-two) feature is enabled, additional
264//! flags are added:
265//! - The radix for the significant digits (default `10`).
266//! - The radix for the exponent base (default `10`).
267//! - The radix for the exponent digits (default `10`).
268//!
269//! When the [`format`](#format) feature is enabled, numerous other syntax and
270//! digit separator flags are enabled, including:
271//! - Requiring or ommitting `+` signs.
272//! - If to use exponent notation.
273//!
274//! Many pre-defined constants therefore exist to simplify common use-cases,
275//! including:
276//! - [`JSON`], [`XML`], [`TOML`], [`YAML`], [`SQLite`], and many more.
277//! - [`Rust`], [`Python`], [`C#`], [`FORTRAN`], [`COBOL`] literals and strings,
278//!   and many more.
279//!
280//! For a list of all supported fields, see [Write
281//! Float Fields][NumberFormatBuilder#write-float-fields].
282//!
283//! <!-- Spacer for rustfmt -->
284#![cfg_attr(
285    feature = "format",
286    doc = "
287[`JSON`]: format::JSON
288[`XML`]: format::XML
289[`TOML`]: format::TOML
290[`YAML`]: format::YAML
291[`SQLite`]: format::SQLITE
292[`Rust`]: format::RUST_LITERAL
293[`Python`]: format::PYTHON_LITERAL
294[`C#`]: format::CSHARP_LITERAL
295[`FORTRAN`]: format::FORTRAN_LITERAL
296[`COBOL`]: format::COBOL_LITERAL
297"
298)]
299#![cfg_attr(
300    not(feature = "format"),
301    doc = "
302[`JSON`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.JSON.html
303[`XML`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.XML.html
304[`TOML`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.TOML.html
305[`YAML`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.YAML.html
306[`SQLite`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.SQLITE.html
307[`Rust`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.RUST_LITERAL.html
308[`Python`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.PYTHON_LITERAL.html
309[`C#`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.CSHARP_LITERAL.html
310[`FORTRAN`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.FORTRAN_LITERAL.html
311[`COBOL`]: https://docs.rs/lexical-write-float/latest/lexical_write_float/format/constant.COBOL_LITERAL.html
312"
313)]
314//!
315//! ## Options API
316//!
317//! The Options API provides high-level options to specify number parsing
318//! or writing, options not intrinsically tied to a number format.
319//! For example, the Options API provides:
320//! - The [`exponent`][Options::exponent] character (defaults to `b'e'` or
321//!   `b'^'`, depending on the radix).
322//! - The [`decimal point`][Options::decimal_point] character (defaults to
323//!   `b'.'`).
324//! - Custom [`NaN`][f64::NAN] and [`Infinity`][f64::INFINITY] string
325//!   [`representations`][Options::nan_string].
326//! - Whether to [`trim`][Options::trim_floats] the fraction component from
327//!   integral floats.
328//! - The exponent [`break-point`][Options::positive_exponent_break] for
329//!   scientific notation.
330//! - The [`maximum`][Options::max_significant_digits] and
331//!   [`minimum`][Options::min_significant_digits] number of significant digits
332//!   to write.
333//! - The rounding [`mode`][Options::round_mode] when truncating significant
334//!   digits while writing.
335//!
336//! In addition, pre-defined constants for each category of options may
337//! be found in their respective modules, for example, [`JSON`][`JSON-OPTS`].
338//!
339//! [`JSON-OPTS`]: options::JSON
340//!
341//! ## Examples
342//!
343//! An example of creating your own options to parse European-style
344//! numbers (which use commas as decimal points, controlling the number
345//! of significant digits, special number representations, and more, is as
346//! follows:
347//!
348//! ```rust
349//! # use core::{num, str};
350//! use lexical_write_float::{FormattedSize, Options, ToLexicalWithOptions};
351//!
352//! const FORMAT: u128 = lexical_write_float::format::STANDARD;
353//! const CUSTOM: Options = Options::builder()
354//!     // write exponents as "1.2^10" and not "1.2e10"
355//!     .exponent(b'^')
356//!     // use the European decimal point, so "1,2" and not "1.2"
357//!     .decimal_point(b',')
358//!     // write NaN and Infinity using the following formats
359//!     .nan_string(Some(b"nan"))
360//!     .inf_string(Some(b"inf"))
361//!     // set the minimum and maximum number of significant digits to write;
362//!     .min_significant_digits(num::NonZeroUsize::new(3))
363//!     .max_significant_digits(num::NonZeroUsize::new(5))
364//!     .build_strict();
365//!
366//! const BUFFER_SIZE: usize = CUSTOM.buffer_size_const::<f64, FORMAT>();
367//! let mut buffer = [0u8; BUFFER_SIZE];
368//!
369//! // write 4 digits, no exponent notation
370//! let digits = 1.234f64.to_lexical_with_options::<FORMAT>(&mut buffer, &CUSTOM);
371//! assert_eq!(str::from_utf8(digits), Ok("1,234"));
372//!
373//! // write 6 digits, rounding to 5
374//! let digits = 1.23456f64.to_lexical_with_options::<FORMAT>(&mut buffer, &CUSTOM);
375//! assert_eq!(str::from_utf8(digits), Ok("1,2346"));
376//!
377//! // write 6 digits, rounding to 5, with exponent notation
378//! let digits = 1.23456e300f64.to_lexical_with_options::<FORMAT>(&mut buffer, &CUSTOM);
379//! assert_eq!(str::from_utf8(digits), Ok("1,2346^300"));
380//!
381//! // write 4 digits, no exponent notation
382//! let digits = 1.2f64.to_lexical_with_options::<FORMAT>(&mut buffer, &CUSTOM);
383//! assert_eq!(str::from_utf8(digits), Ok("1,20"));
384//!
385//! // write a literal NaN string
386//! let digits = f64::NAN.to_lexical_with_options::<FORMAT>(&mut buffer, &CUSTOM);
387//! assert_eq!(str::from_utf8(digits), Ok("nan"));
388//!
389//! // write a literal +Infinity string
390//! let digits = f64::INFINITY.to_lexical_with_options::<FORMAT>(&mut buffer, &CUSTOM);
391//! assert_eq!(str::from_utf8(digits), Ok("inf"));
392//! ```
393//!
394//! # Higher-Level APIs
395//!
396//! If you would like support for writing to [`String`] directly, use
397//! [`lexical`] instead. If you would like an API that supports multiple numeric
398//! conversions rather than just writing integers, use [`lexical-core`] instead.
399//!
400//! [`lexical`]: https://crates.io/crates/lexical
401//! [`lexical-core`]: https://crates.io/crates/lexical-core
402//!
403//! # Version Support
404//!
405//! The minimum, standard, required version is [`1.63.0`][`rust-1.63.0`], for
406//! const generic support. Older versions of lexical support older Rust
407//! versions.
408//!
409//! # Algorithms
410//!
411//! There's currently 5 algorithms used, depending on the requirements.
412//!
413//! 1. Compact for decimal strings uses the Grisu algorithm.
414//! 2. An optimized algorithm based on the Dragonbox algorithm.
415//! 3. An optimized algorithm for formatting to string with power-of-two
416//!    radixes.
417//! 4. An optimized algorithm for hexadecimal floats.
418//! 5. A fallback algorithm for all other radixes.
419//!
420//! The Grisu algorithm is based on "Printing Floating-Point Numbers Quickly
421//! and Accurately with Integers", by Florian Loitsch, available online
422//! [here](https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf).
423//! The dragonbox algorithm is based on the reference C++ implementation,
424//! hosted [here](https://github.com/jk-jeon/dragonbox/), and the algorithm
425//! is described in depth
426//! [here](https://github.com/jk-jeon/dragonbox/blob/master/other_files/Dragonbox.pdf).
427//! The radix algorithm is adapted from the V8 codebase, and may be found
428//! [here](https://github.com/v8/v8).
429//!
430//! # Design
431//!
432//! - [Algorithm Approach](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-float/docs/Algorithm.md)
433//! - [Benchmarks](https://github.com/Alexhuszagh/rust-lexical/blob/main/lexical-write-float/docs/Benchmarks.md)
434//! - [Comprehensive Benchmarks](https://github.com/Alexhuszagh/lexical-benchmarks)
435//!
436//! [`rust-1.63.0`]: https://blog.rust-lang.org/2022/08/11/Rust-1.63.0.html
437//! [`String`]: https://doc.rust-lang.org/alloc/string/struct.String.html
438//! [`to_string`]: https://doc.rust-lang.org/alloc/string/trait.ToString.html#tymethod.to_string
439
440// We want to have the same safety guarantees as Rust core,
441// so we allow unused unsafe to clearly document safety guarantees.
442#![allow(unused_unsafe)]
443#![cfg_attr(feature = "lint", warn(unsafe_op_in_unsafe_fn))]
444#![cfg_attr(not(feature = "std"), no_std)]
445#![cfg_attr(docsrs, feature(doc_cfg))]
446#![cfg_attr(docsrs, feature(doc_auto_cfg))]
447#![deny(
448    clippy::doc_markdown,
449    clippy::unnecessary_safety_comment,
450    clippy::semicolon_if_nothing_returned,
451    clippy::unwrap_used,
452    clippy::as_underscore
453)]
454#![allow(
455    // used when concepts are logically separate
456    clippy::match_same_arms,
457    // loss of precision is intentional
458    clippy::integer_division,
459    // mathematical names use 1-character identifiers
460    clippy::min_ident_chars,
461    // these are not cryptographically secure contexts
462    clippy::integer_division_remainder_used,
463    // this can be intentional
464    clippy::module_name_repetitions,
465    // this is intentional: already passing a pointer and need performance
466    clippy::needless_pass_by_value,
467    // we use this for inline formatting for unsafe blocks
468    clippy::semicolon_inside_block,
469)]
470
471#[macro_use]
472mod index;
473#[macro_use]
474mod shared;
475
476pub mod algorithm;
477pub mod binary;
478pub mod compact;
479pub mod float;
480pub mod hex;
481pub mod options;
482pub mod radix;
483pub mod table;
484pub mod write;
485
486mod api;
487mod table_dragonbox;
488mod table_grisu;
489
490// Re-exports
491#[cfg(feature = "f16")]
492pub use lexical_util::bf16::bf16;
493pub use lexical_util::constants::{FormattedSize, BUFFER_SIZE};
494pub use lexical_util::error::Error;
495#[cfg(feature = "f16")]
496pub use lexical_util::f16::f16;
497pub use lexical_util::format::{self, NumberFormat, NumberFormatBuilder};
498pub use lexical_util::options::WriteOptions;
499pub use lexical_util::result::Result;
500
501pub use self::api::{ToLexical, ToLexicalWithOptions};
502#[doc(inline)]
503pub use self::options::{Options, OptionsBuilder, RoundMode};