use crate::{ConversionResult, FloatLiteral, ParseError, ParseErrorKind};
macro_rules! assert_eq_float {
($left: expr, $right: expr) => {
let left_val: f32 = $left;
let right_val: f32 = $right;
if left_val.to_bits() != right_val.to_bits() {
panic!(
r#"float assertion failed: `(left == right)`
left: `{:?}` (`{:08x}`)
right: `{:?}` (`{:08x}`)"#,
left_val,
left_val.to_bits(),
right_val,
right_val.to_bits()
);
}
};
}
macro_rules! assert_eq_double {
($left: expr, $right: expr) => {
let left_val: f64 = $left;
let right_val: f64 = $right;
if left_val.to_bits() != right_val.to_bits() {
panic!(
r#"float assertion failed: `(left == right)`
left: `{:?}` (`{:016x}`)
right: `{:?}` (`{:016x}`)"#,
left_val,
left_val.to_bits(),
right_val,
right_val.to_bits()
);
}
};
}
fn test_float(s: &str, result: f32) {
let float_repr = s.parse::<FloatLiteral>().unwrap();
let float_result: f32 = float_repr.convert().inner();
assert_eq_float!(float_result, result);
#[cfg(feature = "std")]
{
let libc_result = libc_funcs::string_to_f32(s.as_ref()).unwrap();
assert_eq_float!(float_result, libc_result);
}
}
fn test_double(s: &str, result: f64) {
let float_repr = s.parse::<FloatLiteral>().unwrap();
let double_result: f64 = float_repr.convert().inner();
assert_eq_double!(double_result, result);
#[cfg(feature = "std")]
{
let libc_result = libc_funcs::string_to_f64(s.as_ref()).unwrap();
assert_eq_double!(double_result, libc_result);
}
}
fn test_both(s: &str, float_result: f32) {
let double_result = float_result as f64;
test_float(s, float_result);
test_double(s, double_result);
}
fn test_parse_error_kind(s: &str, error: ParseErrorKind) {
assert_eq!(s.parse::<FloatLiteral>().unwrap_err().kind, error);
}
fn test_parse_error(s: &str, error: ParseError) {
assert_eq!(s.parse::<FloatLiteral>().unwrap_err(), error);
}
fn test_imprecise(s: &str) {
let float_literal = s.parse::<FloatLiteral>().unwrap();
let conversion_result = float_literal.convert::<f32>();
if let ConversionResult::Imprecise(_) = conversion_result {
} else {
panic!(
"conversion from {:?} to float should have been imprecise (was {:?})",
s, conversion_result
);
}
}
#[test]
fn test_zero() {
test_both("0x0", 0.0);
test_both("0x0.", 0.0);
test_both("0x.0", 0.0);
test_both("0x0.0", 0.0);
test_both("0x0000.0000", 0.0);
}
#[test]
fn test_integers() {
test_both("0x11", 17.0);
test_both("0x21", 33.0);
test_both("0x22", 34.0);
test_both("0xDEAD", 57005.0);
test_both("0xBEEF", 48879.0);
}
#[test]
fn test_fractions() {
test_both("0x0.2", 0.125);
test_both("0x0.4", 0.25);
test_both("0x0.8", 0.5);
test_both("0x0.c", 0.75);
test_both("0x0.e", 0.875);
}
#[test]
fn test_exponents() {
test_both("0x0.01", 0.003_906_25);
test_both("0x0.1", 0.0625);
test_both("0x1", 1.0);
test_both("0x10", 16.0);
test_both("0x100", 256.0);
test_both("0x1p-8", 0.003_906_25);
test_both("0x1p-4", 0.0625);
test_both("0x1p0", 1.0);
test_both("0x1p4", 16.0);
test_both("0x1p8", 256.0);
test_both("0x0.01p8", 1.0);
test_both("0x0.1p4", 1.0);
test_both("0x1p0", 1.0);
test_both("0x10p-4", 1.0);
test_both("0x100p-8", 1.0);
}
#[test]
fn test_overflow_underflow() {
test_float("0x1p1000", core::f32::INFINITY);
test_float("-0x1p1000", core::f32::NEG_INFINITY);
test_float("0x1p-1000", 0.0);
test_float("-0x1p-1000", -0.0);
}
#[test]
#[ignore]
fn test_subnormal() {
test_float("0x1p-128", 0.0);
test_float("-0x1p-128", -0.0);
}
#[test]
fn rcc_tests() {
test_both("0x.ep0", 0.875);
test_both("0x.ep-0", 0.875);
test_both("0xe.p-4", 0.875);
test_both("0xep-4", 0.875);
"0x.000000000000000000102".parse::<FloatLiteral>().unwrap();
}
#[test]
fn test_incomplete() {
test_parse_error_kind("", ParseErrorKind::MissingPrefix);
test_parse_error_kind("-", ParseErrorKind::MissingPrefix);
test_parse_error_kind("+", ParseErrorKind::MissingPrefix);
test_parse_error_kind("-3.2", ParseErrorKind::MissingPrefix);
test_parse_error_kind("0x", ParseErrorKind::MissingDigits);
test_parse_error_kind("-0x", ParseErrorKind::MissingDigits);
test_parse_error_kind("+0x", ParseErrorKind::MissingDigits);
test_parse_error_kind("0x.", ParseErrorKind::MissingDigits);
test_parse_error_kind("0xp", ParseErrorKind::MissingDigits);
test_parse_error_kind("0x.p1", ParseErrorKind::MissingDigits);
test_parse_error_kind("0x1p", ParseErrorKind::MissingExponent);
test_parse_error_kind("0x1p+", ParseErrorKind::MissingExponent);
test_parse_error_kind("0x1p-", ParseErrorKind::MissingExponent);
test_parse_error_kind("0x1p10000000000", ParseErrorKind::ExponentOverflow);
test_parse_error_kind("0x1p-10000000000", ParseErrorKind::ExponentOverflow);
test_parse_error_kind("0xbaddata", ParseErrorKind::MissingEnd);
}
#[test]
fn test_fuzzer_finds() {
"0X.0000002".parse::<FloatLiteral>().unwrap();
let literal = "0x3p127".parse::<FloatLiteral>().unwrap();
assert!(!literal.convert::<f32>().inner().is_nan());
}
#[test]
fn test_zero_trimming() {
test_both("0x0.0000000001p+40", 1.0);
test_both("0x10000000000p-40", 1.0);
"0x10000000000".parse::<FloatLiteral>().unwrap();
"0x.0000000001".parse::<FloatLiteral>().unwrap();
}
#[test]
fn test_double_precision() {
test_float("0x1000000001", 68_719_480_000.0);
test_double("0x1000000001", 68_719_476_737.0);
}
#[test]
fn test_imprecise_conversions() {
test_imprecise("0x1p-10000"); test_imprecise("0x123456789abcdef"); test_imprecise("0x1p10000"); }
#[test]
fn test_error_indecies() {
test_parse_error("0.F", ParseErrorKind::MissingPrefix.at(0));
test_parse_error("0x.", ParseErrorKind::MissingDigits.at(3));
test_parse_error("0x.p1", ParseErrorKind::MissingDigits.at(3));
test_parse_error("0xb.0p", ParseErrorKind::MissingExponent.at(6));
test_parse_error("0x1p-", ParseErrorKind::MissingExponent.at(4));
test_parse_error("0x1p3000000000", ParseErrorKind::ExponentOverflow.at(4));
test_parse_error("0x1.g", ParseErrorKind::MissingEnd.at(4));
}
#[cfg(feature = "std")]
mod libc_funcs {
use std::ffi;
#[allow(unsafe_code)]
pub fn f64_to_string(f: f64) -> Result<Vec<u8>, ()> {
let mut dest = [0u8; 32];
let format = ffi::CString::new("%a").unwrap();
let number = f as libc::c_double;
let check =
unsafe { libc::snprintf(dest.as_mut_ptr() as *mut i8, 32, format.as_ptr(), number) };
if check >= 0 && check < 32 {
Ok(dest[..check as usize].to_vec())
} else {
Err(())
}
}
#[allow(unsafe_code)]
pub fn string_to_f32(string: &[u8]) -> Result<f32, ()> {
let source = ffi::CString::new(string).unwrap();
let format = ffi::CString::new("%a").unwrap();
let mut dest: f32 = 0.0;
let check = unsafe { libc::sscanf(source.as_ptr(), format.as_ptr(), &mut dest) };
if check == 1 {
Ok(dest)
} else {
Err(())
}
}
#[allow(unsafe_code)]
pub fn string_to_f64(string: &[u8]) -> Result<f64, ()> {
let source = ffi::CString::new(string).unwrap();
let format = ffi::CString::new("%la").unwrap();
let mut dest: f64 = 0.0;
let check = unsafe { libc::sscanf(source.as_ptr(), format.as_ptr(), &mut dest) };
if check == 1 {
Ok(dest)
} else {
Err(())
}
}
}