use std::ops::{Add, Div, Mul, Sub};
pub trait Float:
sealed::Sealed
+ Copy
+ Add<Output = Self>
+ Sub<Output = Self>
+ Mul<Output = Self>
+ Div<Output = Self>
{
#[must_use]
fn from_f32(v: f32) -> Self;
#[must_use]
fn from_f64(v: f64) -> Self;
#[must_use]
fn to_f32(self) -> f32;
#[must_use]
fn to_f64(self) -> f64;
#[must_use]
fn exp(self) -> Self;
#[must_use]
fn log10(self) -> Self;
#[must_use]
fn powf(self, exp: Self) -> Self;
}
pub trait Sample: Float + Default + Send + Sync + 'static {}
impl Sample for f32 {}
impl Sample for f64 {}
mod sealed {
pub trait Sealed {}
impl Sealed for f32 {}
impl Sealed for f64 {}
}
impl Float for f32 {
#[inline]
fn from_f32(v: f32) -> Self {
v
}
#[inline]
#[allow(clippy::cast_possible_truncation)]
fn from_f64(v: f64) -> Self {
debug_assert!(
!v.is_nan(),
"Float::from_f64: NaN narrowed to f32 - DSP loop or coefficient \
computation produced an undefined value?",
);
v as f32
}
#[inline]
fn to_f32(self) -> f32 {
self
}
#[inline]
fn to_f64(self) -> f64 {
f64::from(self)
}
#[inline]
fn exp(self) -> Self {
f32::exp(self)
}
#[inline]
fn log10(self) -> Self {
f32::log10(self)
}
#[inline]
fn powf(self, exp: Self) -> Self {
f32::powf(self, exp)
}
}
impl Float for f64 {
#[inline]
fn from_f32(v: f32) -> Self {
f64::from(v)
}
#[inline]
fn from_f64(v: f64) -> Self {
v
}
#[inline]
#[allow(clippy::cast_possible_truncation)]
fn to_f32(self) -> f32 {
debug_assert!(
!self.is_nan(),
"Float::to_f32: NaN narrowed to f32 - DSP loop or coefficient \
computation produced an undefined value?",
);
self as f32
}
#[inline]
fn to_f64(self) -> f64 {
self
}
#[inline]
fn exp(self) -> Self {
f64::exp(self)
}
#[inline]
fn log10(self) -> Self {
f64::log10(self)
}
#[inline]
fn powf(self, exp: Self) -> Self {
f64::powf(self, exp)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(clippy::float_cmp)]
fn widen_narrow_round_trip_f32() {
let v: f32 = 0.123_456_7;
assert_eq!(f32::from_f64(v.to_f64()), v);
}
#[test]
fn widen_narrow_round_trip_f64_lossy() {
let v: f64 = 0.123_456_789_012_345;
let round_tripped = f32::from_f64(v).to_f64();
assert!((round_tripped - v).abs() < 1e-7);
}
#[test]
#[should_panic(expected = "NaN narrowed to f32")]
#[cfg(debug_assertions)]
fn nan_narrow_debug_panics() {
let _ = f32::from_f64(f64::NAN);
}
}