1use std::ops::{Add, Div, Mul, Sub};
13
14pub trait Float:
18 sealed::Sealed
19 + Copy
20 + Add<Output = Self>
21 + Sub<Output = Self>
22 + Mul<Output = Self>
23 + Div<Output = Self>
24{
25 const IS_F64: bool;
27
28 #[must_use]
30 fn from_f32(v: f32) -> Self;
31
32 #[must_use]
35 fn from_f64(v: f64) -> Self;
36
37 #[must_use]
39 fn to_f32(self) -> f32;
40
41 #[must_use]
43 fn to_f64(self) -> f64;
44
45 #[must_use]
46 fn exp(self) -> Self;
47
48 #[must_use]
49 fn log10(self) -> Self;
50
51 #[must_use]
52 fn powf(self, exp: Self) -> Self;
53}
54
55pub trait Sample: Float + Default + Send + Sync + 'static {}
57
58impl Sample for f32 {}
59impl Sample for f64 {}
60
61mod sealed {
62 pub trait Sealed {}
63 impl Sealed for f32 {}
64 impl Sealed for f64 {}
65}
66
67impl Float for f32 {
68 const IS_F64: bool = false;
69
70 #[inline]
71 fn from_f32(v: f32) -> Self {
72 v
73 }
74
75 #[inline]
76 #[allow(clippy::cast_possible_truncation)]
77 fn from_f64(v: f64) -> Self {
78 debug_assert!(
79 !v.is_nan(),
80 "Float::from_f64: NaN narrowed to f32 - DSP loop or coefficient \
81 computation produced an undefined value?",
82 );
83 v as f32
84 }
85
86 #[inline]
87 fn to_f32(self) -> f32 {
88 self
89 }
90
91 #[inline]
92 fn to_f64(self) -> f64 {
93 f64::from(self)
94 }
95
96 #[inline]
97 fn exp(self) -> Self {
98 f32::exp(self)
99 }
100 #[inline]
101 fn log10(self) -> Self {
102 f32::log10(self)
103 }
104 #[inline]
105 fn powf(self, exp: Self) -> Self {
106 f32::powf(self, exp)
107 }
108}
109
110impl Float for f64 {
111 const IS_F64: bool = true;
112
113 #[inline]
114 fn from_f32(v: f32) -> Self {
115 f64::from(v)
116 }
117
118 #[inline]
119 fn from_f64(v: f64) -> Self {
120 v
121 }
122
123 #[inline]
124 #[allow(clippy::cast_possible_truncation)]
125 fn to_f32(self) -> f32 {
126 debug_assert!(
127 !self.is_nan(),
128 "Float::to_f32: NaN narrowed to f32 - DSP loop or coefficient \
129 computation produced an undefined value?",
130 );
131 self as f32
132 }
133
134 #[inline]
135 fn to_f64(self) -> f64 {
136 self
137 }
138
139 #[inline]
140 fn exp(self) -> Self {
141 f64::exp(self)
142 }
143 #[inline]
144 fn log10(self) -> Self {
145 f64::log10(self)
146 }
147 #[inline]
148 fn powf(self, exp: Self) -> Self {
149 f64::powf(self, exp)
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 #[allow(clippy::float_cmp)]
159 fn widen_narrow_round_trip_f32() {
160 let v: f32 = 0.123_456_7;
161 assert_eq!(f32::from_f64(v.to_f64()), v);
162 }
163
164 #[test]
165 fn widen_narrow_round_trip_f64_lossy() {
166 let v: f64 = 0.123_456_789_012_345;
167 let round_tripped = f32::from_f64(v).to_f64();
168 assert!((round_tripped - v).abs() < 1e-7);
169 }
170
171 #[test]
172 #[should_panic(expected = "NaN narrowed to f32")]
173 #[cfg(debug_assertions)]
174 fn nan_narrow_debug_panics() {
175 let _ = f32::from_f64(f64::NAN);
176 }
177}