lexical_write_float/
write.rs

1//! Shared trait and methods for writing floats.
2
3#![doc(hidden)]
4
5#[cfg(not(feature = "compact"))]
6use crate::algorithm::write_float as write_float_decimal;
7#[cfg(feature = "power-of-two")]
8use crate::binary;
9/// Select the back-end.
10#[cfg(feature = "compact")]
11use crate::compact::write_float as write_float_decimal;
12#[cfg(feature = "power-of-two")]
13use crate::hex;
14#[cfg(feature = "radix")]
15use crate::radix;
16
17use crate::float::RawFloat;
18use crate::options::Options;
19#[cfg(feature = "f16")]
20use lexical_util::bf16::bf16;
21use lexical_util::constants::FormattedSize;
22#[cfg(feature = "f16")]
23use lexical_util::f16::f16;
24use lexical_util::format::NumberFormat;
25use lexical_write_integer::write::WriteInteger;
26
27/// Write float trait.
28pub trait WriteFloat: RawFloat {
29    /// Forward write integer parameters to an unoptimized backend.
30    ///
31    /// # Safety
32    ///
33    /// Safe as long as the buffer can hold [`FORMATTED_SIZE`] elements
34    /// (or [`FORMATTED_SIZE_DECIMAL`] for decimal). If using custom digit
35    /// precision control (such as specifying a minimum number of significant
36    /// digits), or disabling scientific notation, then more digits may be
37    /// required (up to `1075` for the leading or trailing zeros, `1` for
38    /// the sign and `1` for the decimal point). So,
39    /// `1077 + min_significant_digits.max(52)`, so ~1200 for a reasonable
40    /// threshold.
41    ///
42    /// # Panics
43    ///
44    /// Panics if the number format is invalid, or if scientific notation
45    /// is used and the exponent base does not equal the mantissa radix
46    /// and the format is not a hexadecimal float. It also panics
47    /// if `options.nan_string` or `options.inf_string` is None and asked
48    /// to serialize a NaN or Inf value.
49    ///
50    /// [`FORMATTED_SIZE`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE
51    /// [`FORMATTED_SIZE_DECIMAL`]: lexical_util::constants::FormattedSize::FORMATTED_SIZE_DECIMAL
52    #[inline]
53    unsafe fn write_float<const FORMAT: u128>(self, bytes: &mut [u8], options: &Options) -> usize
54    where
55        Self::Unsigned: FormattedSize + WriteInteger,
56    {
57        // Validate our format options.
58        let format = NumberFormat::<FORMAT> {};
59        assert!(format.is_valid());
60        // Avoid any false assumptions for 128-bit floats.
61        assert!(Self::BITS <= 64);
62
63        #[cfg(feature = "power-of-two")]
64        {
65            if format.radix() != format.exponent_base() {
66                assert!(matches!(
67                    (format.radix(), format.exponent_base()),
68                    (4, 2) | (8, 2) | (16, 2) | (32, 2) | (16, 4)
69                ));
70            }
71        }
72
73        let (float, count, bytes) = if self < Self::ZERO {
74            // SAFETY: safe if `bytes.len() > 1`.
75            unsafe { index_unchecked_mut!(bytes[0]) = b'-' };
76            (-self, 1, unsafe { &mut index_unchecked_mut!(bytes[1..]) })
77        } else if cfg!(feature = "format") && format.required_mantissa_sign() {
78            // SAFETY: safe if `bytes.len() > 1`.
79            unsafe { index_unchecked_mut!(bytes[0]) = b'+' };
80            (self, 1, unsafe { &mut index_unchecked_mut!(bytes[1..]) })
81        } else {
82            (self, 0, bytes)
83        };
84
85        // Handle special values.
86        if !self.is_special() {
87            #[cfg(all(feature = "power-of-two", not(feature = "radix")))]
88            {
89                // SAFETY: safe if the buffer can hold the significant digits
90                let radix = format.radix();
91                let exponent_base = format.exponent_base();
92                count
93                    + if radix == 10 {
94                        unsafe { write_float_decimal::<_, FORMAT>(float, bytes, options) }
95                    } else if radix != exponent_base {
96                        unsafe { hex::write_float::<_, FORMAT>(float, bytes, options) }
97                    } else {
98                        unsafe { binary::write_float::<_, FORMAT>(float, bytes, options) }
99                    }
100            }
101
102            #[cfg(feature = "radix")]
103            {
104                // SAFETY: safe if the buffer can hold the significant digits
105                let radix = format.radix();
106                let exponent_base = format.exponent_base();
107                count
108                    + if radix == 10 {
109                        unsafe { write_float_decimal::<_, FORMAT>(float, bytes, options) }
110                    } else if radix != exponent_base {
111                        unsafe { hex::write_float::<_, FORMAT>(float, bytes, options) }
112                    } else if matches!(radix, 2 | 4 | 8 | 16 | 32) {
113                        unsafe { binary::write_float::<_, FORMAT>(float, bytes, options) }
114                    } else {
115                        unsafe { radix::write_float::<_, FORMAT>(float, bytes, options) }
116                    }
117            }
118
119            #[cfg(not(feature = "power-of-two"))]
120            {
121                // SAFETY: safe if the buffer can hold the significant digits
122                count + unsafe { write_float_decimal::<_, FORMAT>(float, bytes, options) }
123            }
124        } else if self.is_nan() {
125            // SAFETY: safe if the buffer is longer than the NaN string.
126            // The NaN string must be <= 50 characters, so safe as long as
127            // the options were build using safe methods.
128            if let Some(nan_string) = options.nan_string() {
129                let length = nan_string.len();
130                unsafe {
131                    let src = nan_string.as_ptr();
132                    let dst = &mut index_unchecked_mut!(bytes[..length]);
133                    copy_nonoverlapping_unchecked!(dst, src, length);
134                }
135                count + length
136            } else {
137                // PANIC: cannot serialize NaN.
138                panic!("NaN explicitly disabled but asked to write NaN as string.");
139            }
140        } else {
141            // is_inf
142            // SAFETY: safe if the buffer is longer than the Inf string.
143            // The Inf string must be <= 50 characters, so safe as long as
144            // the options were build using safe methods.
145            if let Some(inf_string) = options.inf_string() {
146                let length = inf_string.len();
147                unsafe {
148                    let src = inf_string.as_ptr();
149                    let dst = &mut index_unchecked_mut!(bytes[..length]);
150                    copy_nonoverlapping_unchecked!(dst, src, length);
151                }
152                count + length
153            } else {
154                // PANIC: cannot serialize inf.
155                panic!("Inf explicitly disabled but asked to write Inf as string.");
156            }
157        }
158    }
159}
160
161macro_rules! write_float_impl {
162    ($($t:ty)*) => ($(
163        impl WriteFloat for $t {}
164    )*)
165}
166
167write_float_impl! { f32 f64 }
168
169#[cfg(feature = "f16")]
170macro_rules! write_float_as_f32 {
171    ($($t:ty)*) => ($(
172        impl WriteFloat for $t {
173            #[inline]
174            unsafe fn write_float<const FORMAT: u128>(self, bytes: &mut [u8], options: &Options) -> usize
175            {
176                // SAFETY: safe if `bytes` is large enough to hold the written bytes.
177                unsafe { self.as_f32().write_float::<FORMAT>(bytes, options) }
178            }
179        }
180    )*)
181}
182
183#[cfg(feature = "f16")]
184write_float_as_f32! { bf16 f16 }