use core::cmp::min;
use core::fmt::{Display, Write};
use core::num::FpCategory;
use crate::float::{FloatBits, MantissaOps as _};
use crate::HexFloat;
impl<F> Display for HexFloat<F>
where
F: FloatBits + Display,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let (sign, exponent, mantissa) = self.to_parts();
match self.category() {
FpCategory::Nan | FpCategory::Infinite => {
return self.0.fmt(f);
}
FpCategory::Zero => {
if sign {
f.write_char('-')?;
}
return f.write_str("0x0.0p0");
}
_ => {}
};
let bias = i32::from(F::EXPONENT_BIAS);
let mshift = (4 - u32::from(F::MANTISSA_BITS) % 4) % 4;
let mut mantissa = mantissa << mshift;
let trailing_zero_bits = mantissa.trailing_zeroes();
let trailing_zero_chars = trailing_zero_bits / 4;
let mantissa_max_chars = (u32::from(F::MANTISSA_BITS) + 3) / 4;
let precision: u32 = f
.precision()
.unwrap_or(0)
.try_into()
.expect("precision out of range");
let precision = precision.clamp(1, mantissa_max_chars);
let remove_chars_from_right = min(trailing_zero_chars, mantissa_max_chars - precision);
mantissa = mantissa >> (remove_chars_from_right * 4);
let mwidth = (mantissa_max_chars - remove_chars_from_right) as usize;
let sign_char = if sign {
"-"
} else if f.sign_plus() {
"+"
} else {
""
};
let mut exponent: i32 = exponent.into() - bias;
let leading = if exponent == -bias {
exponent += 1;
"0."
} else {
"1."
};
write!(f, "{sign_char}0x{leading}{mantissa:0mwidth$x}p{exponent}")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_f32() {
assert_eq!(HexFloat::from(0.0f32).to_string(), "0x0.0p0");
assert_eq!(HexFloat::from(1.0f32).to_string(), "0x1.0p0");
assert_eq!(HexFloat::from(1.5f32).to_string(), "0x1.8p0");
assert_eq!(HexFloat::from(1.25f32).to_string(), "0x1.4p0");
assert_eq!(HexFloat::from(1.125f32).to_string(), "0x1.2p0");
assert_eq!(HexFloat::from(1.03125f32).to_string(), "0x1.08p0");
assert_eq!(HexFloat::from(1.0000001_f32).to_string(), "0x1.000002p0");
}
#[test]
fn test_format_f64() {
assert_eq!(HexFloat::from(0.0f64).to_string(), "0x0.0p0");
assert_eq!(HexFloat::from(1.0f64).to_string(), "0x1.0p0");
assert_eq!(HexFloat::from(1.5f64).to_string(), "0x1.8p0");
assert_eq!(HexFloat::from(1.25f64).to_string(), "0x1.4p0");
assert_eq!(HexFloat::from(1.125f64).to_string(), "0x1.2p0");
assert_eq!(HexFloat::from(1.03125f64).to_string(), "0x1.08p0");
assert_eq!(
HexFloat::from(1.0000000000000002_f64).to_string(),
"0x1.0000000000001p0"
);
}
#[test]
fn test_format_options() {
assert_eq!(format!("{:+}", HexFloat::from(1.0f32)), "+0x1.0p0");
assert_eq!(format!("{:+}", HexFloat::from(-1.0f32)), "-0x1.0p0");
assert_eq!(format!("{}", HexFloat::from(-1.0f32)), "-0x1.0p0");
assert_eq!(crate::parse::<f32>("+0x1.0p0").unwrap(), 1.0);
}
#[test]
fn test_format_precision() {
let value = crate::parse::<f32>("0x1.111112p0").unwrap();
assert_eq!(format!("{:0.1}", HexFloat::from(value)), "0x1.111112p0");
assert_eq!(format!("{:0.5}", HexFloat::from(value)), "0x1.111112p0");
assert_eq!(format!("{:0.6}", HexFloat::from(value)), "0x1.111112p0");
assert_eq!(format!("{:0.7}", HexFloat::from(value)), "0x1.111112p0");
let value = crate::parse::<f64>("0x1.1111111111111p0").unwrap();
assert_eq!(
format!("{:0.1}", HexFloat::from(value)),
"0x1.1111111111111p0"
);
assert_eq!(
format!("{:0.5}", HexFloat::from(value)),
"0x1.1111111111111p0"
);
assert_eq!(
format!("{:0.13}", HexFloat::from(value)),
"0x1.1111111111111p0"
);
assert_eq!(
format!("{:0.14}", HexFloat::from(value)),
"0x1.1111111111111p0"
);
assert_eq!(format!("{:0.1}", HexFloat::from(1.0f32)), "0x1.0p0");
assert_eq!(format!("{:0.2}", HexFloat::from(1.0f32)), "0x1.00p0");
assert_eq!(format!("{:0.3}", HexFloat::from(1.0f32)), "0x1.000p0");
assert_eq!(format!("{:0.5}", HexFloat::from(1.0f32)), "0x1.00000p0");
assert_eq!(format!("{:0.6}", HexFloat::from(1.0f32)), "0x1.000000p0");
assert_eq!(format!("{:0.7}", HexFloat::from(1.0f32)), "0x1.000000p0");
assert_eq!(format!("{:0.1}", HexFloat::from(1.0f64)), "0x1.0p0");
assert_eq!(format!("{:0.2}", HexFloat::from(1.0f64)), "0x1.00p0");
assert_eq!(format!("{:0.3}", HexFloat::from(1.0f64)), "0x1.000p0");
assert_eq!(
format!("{:0.12}", HexFloat::from(1.0f64)),
"0x1.000000000000p0"
);
assert_eq!(
format!("{:0.13}", HexFloat::from(1.0f64)),
"0x1.0000000000000p0"
);
assert_eq!(
format!("{:0.14}", HexFloat::from(1.0f64)),
"0x1.0000000000000p0"
);
assert_eq!(
format!("{:0.15}", HexFloat::from(1.0f64)),
"0x1.0000000000000p0"
);
}
}