use core::fmt::Debug;
pub trait Float:
Copy
+ Default
+ PartialEq
+ PartialOrd
+ Debug
+ 'static
+ core::ops::Add<Output = Self>
+ core::ops::Sub<Output = Self>
+ core::ops::Mul<Output = Self>
+ core::ops::Div<Output = Self>
+ core::ops::Rem<Output = Self>
+ core::ops::Neg<Output = Self>
{
const BITS_LOG2: u8;
fn from_f64(n: f64) -> Self;
fn to_f64(self) -> f64;
fn add(self, other: Self) -> Self;
fn sub(self, other: Self) -> Self;
fn mul(self, other: Self) -> Self;
fn div(self, other: Self) -> Self;
fn neg(self) -> Self;
}
impl Float for f32 {
const BITS_LOG2: u8 = 5;
fn from_f64(n: f64) -> Self {
n as f32
}
fn to_f64(self) -> f64 {
self as f64
}
fn add(self, other: Self) -> Self {
self + other
}
fn sub(self, other: Self) -> Self {
self - other
}
fn mul(self, other: Self) -> Self {
self * other
}
fn div(self, other: Self) -> Self {
self / other
}
fn neg(self) -> Self {
-self
}
}
impl Float for f64 {
const BITS_LOG2: u8 = 6;
fn from_f64(n: f64) -> Self {
n
}
fn to_f64(self) -> f64 {
self
}
fn add(self, other: Self) -> Self {
self + other
}
fn sub(self, other: Self) -> Self {
self - other
}
fn mul(self, other: Self) -> Self {
self * other
}
fn div(self, other: Self) -> Self {
self / other
}
fn neg(self) -> Self {
-self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn f32_basics() {
assert_eq!(<f32 as Float>::BITS_LOG2, 5);
assert_eq!(<f32 as Float>::from_f64(0.5), 0.5_f32);
assert_eq!((1.0_f32).add(2.0).to_f64(), 3.0);
}
#[test]
fn f64_basics() {
assert_eq!(<f64 as Float>::BITS_LOG2, 6);
assert_eq!(<f64 as Float>::from_f64(0.5), 0.5_f64);
assert_eq!((1.0_f64).add(2.0).to_f64(), 3.0);
}
#[test]
fn f32_narrowing_loses_precision() {
let original_f64 = 1.1_f64;
let narrowed = <f32 as Float>::from_f64(original_f64);
let back_f64 = <f32 as Float>::to_f64(narrowed);
let delta = (back_f64 - original_f64).abs();
assert!(delta < 1e-6, "narrowing lost more than 1e-6: {}", delta);
}
}