use crate::util::*;
#[cfg(feature = "radix")]
use super::radix::{double_radix, float_radix};
cfg_if! {
if #[cfg(feature = "grisu3")] {
use super::grisu3::{double_decimal, float_decimal};
} else if #[cfg(feature = "ryu")] {
use super::ryu::{double_decimal, float_decimal};
} else {
use super::grisu2::{double_decimal, float_decimal};
}}
pub(crate) trait FloatToString: Float {
fn decimal<'a>(self, bytes: &'a mut [u8]) -> usize;
#[cfg(feature = "radix")]
fn radix<'a>(self, radix: u32, bytes: &'a mut [u8]) -> usize;
}
impl FloatToString for f32 {
perftools_inline!{
fn decimal<'a>(self, bytes: &'a mut [u8]) -> usize {
float_decimal(self, bytes)
}}
perftools_inline!{
#[cfg(feature = "radix")]
fn radix<'a>(self, radix: u32, bytes: &'a mut [u8]) -> usize {
float_radix(self, radix, bytes)
}}
}
impl FloatToString for f64 {
perftools_inline!{
fn decimal<'a>(self, bytes: &'a mut [u8]) -> usize {
double_decimal(self, bytes)
}}
perftools_inline!{
#[cfg(feature = "radix")]
fn radix<'a>(self, radix: u32, bytes: &'a mut [u8]) -> usize {
double_radix(self, radix, bytes)
}}
}
perftools_inline!{
fn forward<'a, F: FloatToString>(value: F, radix: u32, bytes: &'a mut [u8])
-> usize
{
debug_assert_radix!(radix);
#[cfg(not(feature = "radix"))] {
value.decimal(bytes)
}
#[cfg(feature = "radix")] {
match radix {
10 => value.decimal(bytes),
_ => value.radix(radix, bytes),
}
}
}}
perftools_inline!{
fn filter_special<'a, F: FloatToString>(value: F, radix: u32, bytes: &'a mut [u8])
-> usize
{
debug_assert!(value.is_sign_positive(), "Value cannot be negative.");
debug_assert_radix!(radix);
#[cfg(not(feature = "trim_floats"))] {
if value.is_zero() {
return copy_to_dst(bytes, b"0.0");
}
}
if value.is_nan() {
copy_to_dst(bytes, get_nan_string())
} else if value.is_special() {
copy_to_dst(bytes, get_inf_string())
} else {
forward(value, radix, bytes)
}
}}
perftools_inline!{
fn filter_sign<'a, F: FloatToString>(value: F, radix: u32, bytes: &'a mut [u8])
-> usize
{
debug_assert_radix!(radix);
#[cfg(feature = "trim_floats")] {
if value.is_zero() {
index_mut!(bytes[0] = b'0');
return 1;
}
}
if value.is_sign_negative() {
let value = -value;
index_mut!(bytes[0] = b'-');
let bytes = &mut index_mut!(bytes[1..]);
filter_special(value, radix, bytes) + 1
} else {
filter_special(value, radix, bytes)
}
}}
perftools_inline!{
fn ftoa<F: FloatToString>(value: F, radix: u32, bytes: &mut [u8])
-> usize
{
let len = filter_sign(value, radix, bytes);
let bytes = &mut index_mut!(bytes[..len]);
trim(bytes)
}}
perftools_inline!{
fn trim<'a>(bytes: &'a mut [u8])
-> usize
{
if cfg!(feature = "trim_floats") && ends_with_slice(bytes, b".0") {
bytes.len() - 2
} else {
bytes.len()
}
}}
to_lexical!(ftoa, f32);
to_lexical!(ftoa, f64);
#[cfg(test)]
mod tests {
use crate::util::*;
use crate::util::test::*;
#[cfg(all(feature = "correct", feature = "property_tests"))]
use quickcheck::quickcheck;
#[cfg(all(feature = "correct", feature = "std", feature = "property_tests"))]
use proptest::{proptest, prop_assert_eq};
use approx::assert_relative_eq;
const F32_DATA : [f32; 31] = [0., 0.1, 1., 1.1, 12., 12.1, 123., 123.1, 1234., 1234.1, 12345., 12345.1, 123456., 123456.1, 1234567., 1234567.1, 12345678., 12345678.1, 123456789., 123456789.1, 123456789.12, 123456789.123, 123456789.1234, 123456789.12345, 1.2345678912345e8, 1.2345e+8, 1.2345e+11, 1.2345e+38, 1.2345e-8, 1.2345e-11, 1.2345e-38];
const F64_DATA: [f64; 33] = [0., 0.1, 1., 1.1, 12., 12.1, 123., 123.1, 1234., 1234.1, 12345., 12345.1, 123456., 123456.1, 1234567., 1234567.1, 12345678., 12345678.1, 123456789., 123456789.1, 123456789.12, 123456789.123, 123456789.1234, 123456789.12345, 1.2345678912345e8, 1.2345e+8, 1.2345e+11, 1.2345e+38, 1.2345e+308, 1.2345e-8, 1.2345e-11, 1.2345e-38, 1.2345e-299];
#[cfg(feature = "radix")]
#[test]
fn f32_binary_test() {
let mut buffer = new_buffer();
#[cfg(feature = "trim_floats")] {
assert_eq!(as_slice(b"0"), 0.0f32.to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"0"), (-0.0f32).to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"1"), 1.0f32.to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"10"), 2.0f32.to_lexical_radix(2, &mut buffer));
}
#[cfg(not(feature = "trim_floats"))] {
assert_eq!(as_slice(b"0.0"), 0.0f32.to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"-0.0"), (-0.0f32).to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"1.0"), 1.0f32.to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"10.0"), 2.0f32.to_lexical_radix(2, &mut buffer));
}
assert_eq!(as_slice(b"1.1"), 1.5f32.to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"1.01"), 1.25f32.to_lexical_radix(2, &mut buffer));
assert_eq!(b"1.001111000000110010", &1.2345678901234567890e0f32.to_lexical_radix(2, &mut buffer)[..20]);
assert_eq!(b"1100.010110000111111", &1.2345678901234567890e1f32.to_lexical_radix(2, &mut buffer)[..20]);
assert_eq!(b"1111011.011101001111", &1.2345678901234567890e2f32.to_lexical_radix(2, &mut buffer)[..20]);
assert_eq!(b"10011010010.10010001", &1.2345678901234567890e3f32.to_lexical_radix(2, &mut buffer)[..20]);
assert_eq!(b"-1.001111000000110010", &(-1.2345678901234567890e0f32).to_lexical_radix(2, &mut buffer)[..21]);
assert_eq!(b"-1100.010110000111111", &(-1.2345678901234567890e1f32).to_lexical_radix(2, &mut buffer)[..21]);
assert_eq!(b"-1111011.011101001111", &(-1.2345678901234567890e2f32).to_lexical_radix(2, &mut buffer)[..21]);
assert_eq!(b"-10011010010.10010001", &(-1.2345678901234567890e3f32).to_lexical_radix(2, &mut buffer)[..21]);
assert_eq!(as_slice(b"NaN"), f32::NAN.to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"inf"), f32::INFINITY.to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"1.1010100000101011110001e-11011"), 0.000000012345f32.to_lexical_radix(2, &mut buffer));
}
#[test]
fn f32_decimal_test() {
let mut buffer = new_buffer();
#[cfg(feature = "trim_floats")] {
assert_eq!(as_slice(b"0"), 0.0f32.to_lexical(&mut buffer));
assert_eq!(as_slice(b"0"), (-0.0f32).to_lexical(&mut buffer));
assert_eq!(as_slice(b"1"), 1.0f32.to_lexical(&mut buffer));
assert_eq!(as_slice(b"10"), 10.0f32.to_lexical(&mut buffer));
}
#[cfg(not(feature = "trim_floats"))] {
assert_eq!(as_slice(b"0.0"), 0.0f32.to_lexical(&mut buffer));
assert_eq!(as_slice(b"-0.0"), (-0.0f32).to_lexical(&mut buffer));
assert_eq!(as_slice(b"1.0"), 1.0f32.to_lexical(&mut buffer));
assert_eq!(as_slice(b"10.0"), 10.0f32.to_lexical(&mut buffer));
}
assert_eq!(as_slice(b"1.234567"), &1.2345678901234567890e0f32.to_lexical(&mut buffer)[..8]);
assert_eq!(as_slice(b"12.34567"), &1.2345678901234567890e1f32.to_lexical(&mut buffer)[..8]);
assert_eq!(as_slice(b"123.4567"), &1.2345678901234567890e2f32.to_lexical(&mut buffer)[..8]);
assert_eq!(as_slice(b"1234.567"), &1.2345678901234567890e3f32.to_lexical(&mut buffer)[..8]);
assert_eq!(as_slice(b"-1.234567"), &(-1.2345678901234567890e0f32).to_lexical(&mut buffer)[..9]);
assert_eq!(as_slice(b"-12.34567"), &(-1.2345678901234567890e1f32).to_lexical(&mut buffer)[..9]);
assert_eq!(as_slice(b"-123.4567"), &(-1.2345678901234567890e2f32).to_lexical(&mut buffer)[..9]);
assert_eq!(as_slice(b"-1234.567"), &(-1.2345678901234567890e3f32).to_lexical(&mut buffer)[..9]);
assert_eq!(as_slice(b"NaN"), f32::NAN.to_lexical(&mut buffer));
assert_eq!(as_slice(b"inf"), f32::INFINITY.to_lexical(&mut buffer));
}
#[test]
fn f32_decimal_roundtrip_test() {
let mut buffer = new_buffer();
for &f in F32_DATA.iter() {
let s = f.to_lexical(&mut buffer);
assert_relative_eq!(f32::from_lexical(s).unwrap(), f, epsilon=1e-6, max_relative=1e-6);
}
}
#[cfg(feature = "radix")]
#[test]
fn f32_radix_roundtrip_test() {
let mut buffer = new_buffer();
for &f in F32_DATA.iter() {
for radix in 2..37 {
let s = f.to_lexical_radix(radix, &mut buffer);
assert_relative_eq!(f32::from_lexical_radix(s, radix).unwrap(), f, max_relative=2e-5);
}
}
}
#[cfg(feature = "radix")]
#[test]
fn f64_binary_test() {
let mut buffer = new_buffer();
#[cfg(feature = "trim_floats")] {
assert_eq!(as_slice(b"0"), 0.0f64.to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"0"), (-0.0f64).to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"1"), 1.0f64.to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"10"), 2.0f64.to_lexical_radix(2, &mut buffer));
}
#[cfg(not(feature = "trim_floats"))] {
assert_eq!(as_slice(b"0.0"), 0.0f64.to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"-0.0"), (-0.0f64).to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"1.0"), 1.0f64.to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"10.0"), 2.0f64.to_lexical_radix(2, &mut buffer));
}
assert_eq!(as_slice(b"1.00111100000011001010010000101000110001"), &1.2345678901234567890e0f64.to_lexical_radix(2, &mut buffer)[..40]);
assert_eq!(as_slice(b"1100.01011000011111100110100110010111101"), &1.2345678901234567890e1f64.to_lexical_radix(2, &mut buffer)[..40]);
assert_eq!(as_slice(b"1111011.01110100111100000001111111101101"), &1.2345678901234567890e2f64.to_lexical_radix(2, &mut buffer)[..40]);
assert_eq!(as_slice(b"10011010010.1001000101100001001111110100"), &1.2345678901234567890e3f64.to_lexical_radix(2, &mut buffer)[..40]);
assert_eq!(as_slice(b"-1.00111100000011001010010000101000110001"), &(-1.2345678901234567890e0f64).to_lexical_radix(2, &mut buffer)[..41]);
assert_eq!(as_slice(b"-1100.01011000011111100110100110010111101"), &(-1.2345678901234567890e1f64).to_lexical_radix(2, &mut buffer)[..41]);
assert_eq!(as_slice(b"-1111011.01110100111100000001111111101101"), &(-1.2345678901234567890e2f64).to_lexical_radix(2, &mut buffer)[..41]);
assert_eq!(as_slice(b"-10011010010.1001000101100001001111110100"), &(-1.2345678901234567890e3f64).to_lexical_radix(2, &mut buffer)[..41]);
assert_eq!(as_slice(b"NaN"), f64::NAN.to_lexical_radix(2, &mut buffer));
assert_eq!(as_slice(b"inf"), f64::INFINITY.to_lexical_radix(2, &mut buffer));
}
#[test]
fn f64_decimal_test() {
let mut buffer = new_buffer();
#[cfg(feature = "trim_floats")] {
assert_eq!(as_slice(b"0"), 0.0.to_lexical(&mut buffer));
assert_eq!(as_slice(b"0"), (-0.0).to_lexical(&mut buffer));
assert_eq!(as_slice(b"1"), 1.0.to_lexical(&mut buffer));
assert_eq!(as_slice(b"10"), 10.0.to_lexical(&mut buffer));
}
#[cfg(not(feature = "trim_floats"))] {
assert_eq!(as_slice(b"0.0"), 0.0.to_lexical(&mut buffer));
assert_eq!(as_slice(b"-0.0"), (-0.0).to_lexical(&mut buffer));
assert_eq!(as_slice(b"1.0"), 1.0.to_lexical(&mut buffer));
assert_eq!(as_slice(b"10.0"), 10.0.to_lexical(&mut buffer));
}
assert_eq!(as_slice(b"1.234567"), &1.2345678901234567890e0.to_lexical(&mut buffer)[..8]);
assert_eq!(as_slice(b"12.34567"), &1.2345678901234567890e1.to_lexical(&mut buffer)[..8]);
assert_eq!(as_slice(b"123.4567"), &1.2345678901234567890e2.to_lexical(&mut buffer)[..8]);
assert_eq!(as_slice(b"1234.567"), &1.2345678901234567890e3.to_lexical(&mut buffer)[..8]);
assert_eq!(as_slice(b"-1.234567"), &(-1.2345678901234567890e0).to_lexical(&mut buffer)[..9]);
assert_eq!(as_slice(b"-12.34567"), &(-1.2345678901234567890e1).to_lexical(&mut buffer)[..9]);
assert_eq!(as_slice(b"-123.4567"), &(-1.2345678901234567890e2).to_lexical(&mut buffer)[..9]);
assert_eq!(as_slice(b"-1234.567"), &(-1.2345678901234567890e3).to_lexical(&mut buffer)[..9]);
assert_eq!(b"NaN".to_vec(), f64::NAN.to_lexical(&mut buffer));
assert_eq!(b"inf".to_vec(), f64::INFINITY.to_lexical(&mut buffer));
}
#[test]
fn f64_decimal_roundtrip_test() {
let mut buffer = new_buffer();
for &f in F64_DATA.iter() {
let s = f.to_lexical(&mut buffer);
assert_relative_eq!(f64::from_lexical(s).unwrap(), f, epsilon=1e-12, max_relative=1e-12);
}
}
#[cfg(feature = "radix")]
#[test]
fn f64_radix_roundtrip_test() {
let mut buffer = new_buffer();
for &f in F64_DATA.iter() {
for radix in 2..37 {
let s = f.to_lexical_radix(radix, &mut buffer);
assert_relative_eq!(f64::from_lexical_radix(s, radix).unwrap(), f, max_relative=3e-5);
}
}
}
#[cfg(all(feature = "correct", feature = "property_tests"))]
quickcheck! {
fn f32_quickcheck(f: f32) -> bool {
let mut buffer = new_buffer();
let parsed = f32::from_lexical(f.to_lexical(&mut buffer)).unwrap();
if f.is_nan() {
parsed.is_nan()
} else {
f == parsed
}
}
fn f64_quickcheck(f: f64) -> bool {
let mut buffer = new_buffer();
let parsed = f64::from_lexical(f.to_lexical(&mut buffer)).unwrap();
if f.is_nan() {
parsed.is_nan()
} else {
f == parsed
}
}
}
#[cfg(all(feature = "correct", feature = "std", feature = "property_tests"))]
proptest! {
#[test]
fn f32_proptest(i in f32::MIN..f32::MAX) {
let mut buffer = new_buffer();
prop_assert_eq!(i, f32::from_lexical(i.to_lexical(&mut buffer)).unwrap());
}
#[test]
fn f64_proptest(i in f64::MIN..f64::MAX) {
let mut buffer = new_buffer();
prop_assert_eq!(i, f64::from_lexical(i.to_lexical(&mut buffer)).unwrap());
}
}
#[test]
#[should_panic]
fn f32_buffer_test() {
let mut buffer = [b'0'; f32::FORMATTED_SIZE_DECIMAL-1];
1.2345f32.to_lexical(&mut buffer);
}
#[test]
#[should_panic]
fn f64_buffer_test() {
let mut buffer = [b'0'; f64::FORMATTED_SIZE_DECIMAL-1];
1.2345f64.to_lexical(&mut buffer);
}
}