use std::cmp::Ordering;
#[derive(Clone, Copy, Debug)]
pub struct Real {
value: f64,
wide: bool,
}
impl Real {
#[must_use]
pub const fn value(self) -> f64 {
self.value
}
#[cfg_attr(
not(any(test, feature = "serde", feature = "binary")),
expect(
dead_code,
reason = "consumed by the serde bridge and the binary codec; dead only when both are compiled out"
)
)]
pub(crate) const fn wide(self) -> bool {
self.wide
}
}
impl From<f32> for Real {
fn from(value: f32) -> Self {
Self {
value: f64::from(value),
wide: false,
}
}
}
impl From<f64> for Real {
fn from(value: f64) -> Self {
Self { value, wide: true }
}
}
impl PartialEq for Real {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl PartialOrd for Real {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.value.partial_cmp(&other.value)
}
}
#[cfg(test)]
mod tests {
#![expect(clippy::float_cmp, reason = "test code: bit-exact float expectations")]
use super::*;
#[test]
fn width_follows_the_source_type() {
assert!(!Real::from(32.0f32).wide());
assert!(Real::from(32.0f64).wide());
}
#[test]
fn narrow_construction_widens_exactly() {
assert_eq!(Real::from(f32::MAX).value(), f64::from(f32::MAX));
assert_eq!(Real::from(0.5f32).value(), 0.5);
}
#[test]
fn equality_is_numeric_and_ignores_width() {
assert_eq!(Real::from(32.0f32), Real::from(32.0f64));
assert_ne!(Real::from(32.0f64), Real::from(64.0f64));
assert_eq!(Real::from(-0.0f64), Real::from(0.0f64));
}
#[test]
fn nan_is_not_equal_to_itself() {
let nan = Real::from(f64::NAN);
assert_ne!(nan, nan);
assert_eq!(nan.partial_cmp(&nan), None);
}
#[test]
fn ordering_is_numeric() {
assert!(Real::from(1.0f32) < Real::from(2.0f64));
assert_eq!(
Real::from(2.0f32).partial_cmp(&Real::from(2.0f64)),
Some(Ordering::Equal)
);
}
}