#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Style {
Typeless,
Data,
Float,
Integer,
}
pub(crate) fn value(bits: u32, style: Style) -> String {
match style {
Style::Float => float(bits),
Style::Integer => integer(bits),
Style::Typeless | Style::Data => {
let exponent = (bits >> 23) & 0xff;
if (exponent == 0 || exponent == 0xff) && bits != 0x8000_0000 {
integer(bits)
} else {
float(bits)
}
}
}
}
pub(crate) fn integer(bits: u32) -> String {
let signed = bits.cast_signed();
if (-10000..=10000).contains(&signed) {
signed.to_string()
} else {
format!("0x{bits:08x}")
}
}
pub(crate) fn float(bits: u32) -> String {
let value = f32::from_bits(bits);
let class = if value.is_infinite() {
Class::Infinite
} else if bits == 0xffc0_0000 {
Class::Indefinite
} else if value.is_nan() {
Class::Quiet
} else {
Class::Finite
};
special(class, bits >> 31 != 0).unwrap_or_else(|| format!("{:.6}", f64::from(value)))
}
pub(crate) fn double(low: u32, high: u32) -> String {
let bits = (u64::from(high) << 32) | u64::from(low);
let value = f64::from_bits(bits);
let class = if value.is_infinite() {
Class::Infinite
} else if bits == 0xfff8_0000_0000_0000 {
Class::Indefinite
} else if value.is_nan() && bits & (1 << 51) == 0 {
Class::Signaling
} else if value.is_nan() {
Class::Quiet
} else {
Class::Finite
};
let text = special(class, high >> 31 != 0).unwrap_or_else(|| format!("{value:.6}"));
format!("{text}l")
}
enum Class {
Finite,
Infinite,
Indefinite,
Quiet,
Signaling,
}
fn special(class: Class, negative: bool) -> Option<String> {
let sign = if negative { "-" } else { "" };
match class {
Class::Finite => None,
Class::Infinite => Some(format!("{sign}1.#INF00")),
Class::Indefinite => Some("-1.#IND00".to_owned()),
Class::Quiet => Some(format!("{sign}1.#QNAN0")),
Class::Signaling => Some(format!("{sign}1.#SNAN0")),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn floats_and_integers() {
assert_eq!(float(0x3f80_0000), "1.000000");
assert_eq!(float(0x7149_f2ca), "1000000015047466219876688855040.000000");
assert_eq!(float(0x8000_0000), "-0.000000");
assert_eq!(float(0x7f80_0000), "1.#INF00");
assert_eq!(integer(0xffff_fc7c), "-900");
assert_eq!(integer(10001), "0x00002711");
assert_eq!(value(0x8000_0000, Style::Typeless), "-0.000000");
assert_eq!(value(1, Style::Typeless), "1");
assert_eq!(value(0x3f00_0000, Style::Typeless), "0.500000");
}
#[test]
fn doubles_keep_a_signaling_nan() {
assert_eq!(double(0, 0xfff0_0000), "-1.#INF00l");
assert_eq!(double(0, 0x7ff0_0000), "1.#INF00l");
assert_eq!(double(1, 0x7ff0_0001), "1.#SNAN0l");
assert_eq!(double(1, 0xfff0_0001), "-1.#SNAN0l");
assert_eq!(double(0, 0xfff8_0000), "-1.#IND00l");
assert_eq!(double(5, 0x7ff8_0000), "1.#QNAN0l");
assert_eq!(double(5, 0xfff8_0000), "-1.#QNAN0l");
}
}