lexical_write_float/
write.rs

1//! Shared trait and methods for writing floats.
2
3#![doc(hidden)]
4
5#[cfg(feature = "f16")]
6use lexical_util::bf16::bf16;
7#[cfg(feature = "f16")]
8use lexical_util::f16::f16;
9use lexical_util::format::NumberFormat;
10use lexical_util::options::WriteOptions;
11use lexical_util::{algorithm::copy_to_dst, constants::FormattedSize};
12use lexical_write_integer::write::WriteInteger;
13
14/// Select the back-end.
15#[cfg(not(feature = "compact"))]
16use crate::algorithm::write_float as write_float_decimal;
17#[cfg(feature = "power-of-two")]
18use crate::binary;
19#[cfg(feature = "compact")]
20use crate::compact::write_float as write_float_decimal;
21use crate::float::RawFloat;
22#[cfg(feature = "power-of-two")]
23use crate::hex;
24use crate::options::Options;
25#[cfg(feature = "radix")]
26use crate::radix;
27
28/// Write an special string to the buffer.
29#[inline(always)]
30fn write_special(bytes: &mut [u8], special: Option<&[u8]>, error: &'static str) -> usize {
31    // The NaN string must be <= 50 characters, so this should never panic.
32    if let Some(special_str) = special {
33        debug_assert!(special_str.len() <= 50, "special_str.len() must be <= 50");
34        copy_to_dst(bytes, special_str)
35    } else {
36        // PANIC: the format does not support serializing that special.
37        panic!("{}", error);
38    }
39}
40
41/// Write an NaN string to the buffer.
42fn write_nan(bytes: &mut [u8], options: &Options, count: usize) -> usize {
43    count
44        + write_special(
45            bytes,
46            options.nan_string(),
47            "NaN explicitly disabled but asked to write NaN as string.",
48        )
49}
50
51/// Write an Inf string to the buffer.
52fn write_inf(bytes: &mut [u8], options: &Options, count: usize) -> usize {
53    count
54        + write_special(
55            bytes,
56            options.inf_string(),
57            "Inf explicitly disabled but asked to write Inf as string.",
58        )
59}
60
61/// Check if a buffer is sufficiently large.
62#[inline(always)]
63fn check_buffer<T, const FORMAT: u128>(len: usize, options: &Options) -> bool
64where
65    T: FormattedSize,
66{
67    let size = Options::buffer_size::<T, FORMAT>(options);
68    len >= size
69}
70
71/// Write float trait.
72pub trait WriteFloat: RawFloat + FormattedSize {
73    /// Forward float writing parameters and write the float.
74    ///
75    /// This abstracts away handling different optimizations and radices into
76    /// a single API.
77    ///
78    /// # Panics
79    ///
80    /// Panics if the number format is invalid, or if scientific notation
81    /// is used and the exponent base does not equal the mantissa radix
82    /// and the format is not a hexadecimal float. It also panics
83    /// if `options.nan_string` or `options.inf_string` is None and asked
84    /// to serialize a NaN or Inf value.
85    ///
86    /// [`FORMATTED_SIZE`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE
87    /// [`FORMATTED_SIZE_DECIMAL`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE_DECIMAL
88    #[cfg_attr(not(feature = "compact"), inline(always))]
89    fn write_float<const FORMAT: u128>(self, bytes: &mut [u8], options: &Options) -> usize
90    where
91        Self::Unsigned: FormattedSize + WriteInteger,
92    {
93        // Validate our format options.
94        assert!(check_buffer::<Self, { FORMAT }>(bytes.len(), options));
95        let format = NumberFormat::<FORMAT> {};
96        assert!(format.is_valid());
97        // Avoid any false assumptions for 128-bit floats.
98        assert!(Self::BITS <= 64);
99
100        #[cfg(feature = "power-of-two")]
101        {
102            // FIXME: I believe this incorrectly handles a few cases.
103            if format.radix() != format.exponent_base() {
104                assert!(matches!(
105                    (format.radix(), format.exponent_base()),
106                    (4, 2) | (8, 2) | (16, 2) | (32, 2) | (16, 4)
107                ));
108            }
109        }
110
111        let (float, count, bytes) = if self.needs_negative_sign() {
112            bytes[0] = b'-';
113            (-self, 1, &mut bytes[1..])
114        } else if cfg!(feature = "format") && format.required_mantissa_sign() {
115            bytes[0] = b'+';
116            (self, 1, &mut bytes[1..])
117        } else {
118            (self, 0, bytes)
119        };
120
121        // Handle special values.
122        if !self.is_special() {
123            #[cfg(all(feature = "power-of-two", not(feature = "radix")))]
124            {
125                let radix = format.radix();
126                let exponent_base = format.exponent_base();
127                count
128                    + if radix == 10 {
129                        write_float_decimal::<_, FORMAT>(float, bytes, options)
130                    } else if radix != exponent_base {
131                        hex::write_float::<_, FORMAT>(float, bytes, options)
132                    } else {
133                        binary::write_float::<_, FORMAT>(float, bytes, options)
134                    }
135            }
136
137            #[cfg(feature = "radix")]
138            {
139                let radix = format.radix();
140                let exponent_base = format.exponent_base();
141                count
142                    + if radix == 10 {
143                        write_float_decimal::<_, FORMAT>(float, bytes, options)
144                    } else if radix != exponent_base {
145                        hex::write_float::<_, FORMAT>(float, bytes, options)
146                    } else if matches!(radix, 2 | 4 | 8 | 16 | 32) {
147                        binary::write_float::<_, FORMAT>(float, bytes, options)
148                    } else {
149                        radix::write_float::<_, FORMAT>(float, bytes, options)
150                    }
151            }
152
153            #[cfg(not(feature = "power-of-two"))]
154            {
155                count + write_float_decimal::<_, FORMAT>(float, bytes, options)
156            }
157        } else if self.is_nan() {
158            write_nan(bytes, options, count)
159        } else {
160            write_inf(bytes, options, count)
161        }
162    }
163}
164
165macro_rules! write_float_impl {
166    ($($t:ty)*) => ($(
167        impl WriteFloat for $t {}
168    )*)
169}
170
171write_float_impl! { f32 f64 }
172
173#[cfg(feature = "f16")]
174macro_rules! write_float_as_f32 {
175    ($($t:ty)*) => ($(
176        impl WriteFloat for $t {
177            #[inline(always)]
178            fn write_float<const FORMAT: u128>(self, bytes: &mut [u8], options: &Options) -> usize
179            {
180                self.as_f32().write_float::<FORMAT>(bytes, options)
181            }
182        }
183    )*)
184}
185
186#[cfg(feature = "f16")]
187write_float_as_f32! { bf16 f16 }