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