1pub(crate) trait RoundedDivision<Rhs = Self> {
7 type Output;
8
9 fn div_round(self, rhs: Rhs) -> Self::Output;
10}
11
12macro_rules! unsigned_rounded_division {
13 ($($t:ty),*) => {
14 $(
15 impl RoundedDivision for $t {
16 type Output = $t;
17
18 fn div_round(self, rhs: Self) -> Self::Output {
19 (self + (rhs >> 1)) / rhs
20 }
21 }
22 )*
23 };
24}
25
26unsigned_rounded_division!(u8, u16, u32, u64, u128, usize);
27
28#[derive(Debug, Eq, PartialEq, Copy, Clone)]
32pub(crate) struct F80 {
33 signed: bool,
34 exponent: u16,
36 fraction: u64,
37}
38
39impl F80 {
40 pub fn from_be_bytes(bytes: [u8; 10]) -> Self {
44 let signed = bytes[0] & 0x80 != 0;
45 let exponent = (u16::from(bytes[0] & 0x7F) << 8) | u16::from(bytes[1]);
46
47 let mut fraction_bytes = [0; 8];
48 fraction_bytes.copy_from_slice(&bytes[2..]);
49 let fraction = u64::from_be_bytes(fraction_bytes);
50
51 Self {
52 signed,
53 exponent,
54 fraction,
55 }
56 }
57
58 pub fn as_f64(&self) -> f64 {
60 let sign = if self.signed { 1 } else { 0 };
70
71 if self.exponent == 32767 {
73 if self.fraction == 0 {
74 return f64::from_bits((sign << 63) | f64::INFINITY.to_bits());
75 }
76
77 return f64::from_bits((sign << 63) | f64::NAN.to_bits());
78 }
79
80 if self.fraction == 0 {
82 return f64::from_bits(sign << 63);
83 }
84
85 let fraction = self.fraction & 0x7FFF_FFFF_FFFF_FFFF;
88 let exponent = self.exponent as i16 - 16383 + 1023;
89 let bits = (sign << 63) | ((exponent as u64) << 52) | (fraction >> 11);
90
91 f64::from_bits(bits)
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[test_log::test]
100 fn test_div_round() {
101 #[derive(Debug)]
102 struct TestEntry {
103 lhs: u32,
104 rhs: u32,
105 result: u32,
106 }
107
108 #[rustfmt::skip]
109 let tests = [
110 TestEntry { lhs: 1, rhs: 1, result: 1 },
111 TestEntry { lhs: 1, rhs: 2, result: 1 },
112 TestEntry { lhs: 2, rhs: 2, result: 1 },
113 TestEntry { lhs: 3, rhs: 2, result: 2 },
114 TestEntry { lhs: 4, rhs: 2, result: 2 },
115 TestEntry { lhs: 5, rhs: 2, result: 3 },
116
117 TestEntry { lhs: 800, rhs: 1500, result: 1 },
119 TestEntry { lhs: 1500, rhs: 3000, result: 1 },
120
121 TestEntry { lhs: 0, rhs: 4000, result: 0 },
123 TestEntry { lhs: 1500, rhs: 4000, result: 0 },
124 ];
125
126 for test in &tests {
127 let result = test.lhs.div_round(test.rhs);
128 assert_eq!(result, test.result, "{}.div_round({})", test.lhs, test.rhs);
129 }
130 }
131
132 #[test_log::test]
133 fn test_f80() {
134 fn cmp_float_nearly_equal(a: f64, b: f64) -> bool {
135 if a.is_infinite() && b.is_infinite() {
136 return true;
137 }
138
139 if a.is_nan() && b.is_nan() {
140 return true;
141 }
142
143 (a - b).abs() < f64::EPSILON
144 }
145
146 #[derive(Debug)]
147 struct TestEntry {
148 input: [u8; 10],
149 output_f64: f64,
150 }
151
152 let tests = [
153 TestEntry {
154 input: [0; 10],
155 output_f64: 0.0,
156 },
157 TestEntry {
158 input: [0x7F, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0],
159 output_f64: f64::INFINITY,
160 },
161 TestEntry {
162 input: [0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0],
163 output_f64: f64::NEG_INFINITY,
164 },
165 TestEntry {
166 input: [0x7F, 0xFF, 0x80, 0, 0, 0, 0, 0, 0, 0],
167 output_f64: f64::NAN,
168 },
169 TestEntry {
170 input: [0xFF, 0xFF, 0x80, 0, 0, 0, 0, 0, 0, 0],
171 output_f64: -f64::NAN,
172 },
173 TestEntry {
174 input: [0x3F, 0xFC, 0x80, 0, 0, 0, 0, 0, 0, 0],
175 output_f64: 0.125,
176 },
177 TestEntry {
178 input: [0x3F, 0xFF, 0x80, 0, 0, 0, 0, 0, 0, 0],
179 output_f64: 1.0,
180 },
181 TestEntry {
182 input: [0x40, 0x00, 0x80, 0, 0, 0, 0, 0, 0, 0],
183 output_f64: 2.0,
184 },
185 TestEntry {
186 input: [0x40, 0x00, 0xC0, 0, 0, 0, 0, 0, 0, 0],
187 output_f64: 3.0,
188 },
189 TestEntry {
190 input: [0x40, 0x0E, 0xBB, 0x80, 0, 0, 0, 0, 0, 0],
191 output_f64: 48000.0,
192 },
193 ];
194
195 for test in &tests {
196 let f80 = F80::from_be_bytes(test.input);
197 let f64 = f80.as_f64();
198 assert!(
199 cmp_float_nearly_equal(f64, test.output_f64),
200 "F80::as_f64({f80:?}) == {f64} (expected {})",
201 test.output_f64
202 );
203 }
204 }
205}