1use crate::elem::{Elem, WrappingAdd};
10
11#[allow(non_camel_case_types)]
13#[derive(Copy, Clone, Default, PartialEq)]
14#[repr(transparent)]
15pub struct f16(u16);
16
17impl f16 {
18 #[inline]
20 pub const fn from_bits(bits: u16) -> Self {
21 f16(bits)
22 }
23
24 #[inline]
26 pub const fn to_bits(self) -> u16 {
27 self.0
28 }
29
30 #[inline]
32 pub fn from_f32(x: f32) -> Self {
33 f16(f32_to_f16(x))
34 }
35
36 #[inline]
41 pub fn to_f32(self) -> f32 {
42 f16_to_f32(self.0)
43 }
44}
45
46impl std::fmt::Debug for f16 {
47 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48 write!(f, "{}", self.to_f32())
49 }
50}
51
52impl From<f32> for f16 {
53 #[inline]
54 fn from(x: f32) -> f16 {
55 f16::from_f32(x)
56 }
57}
58
59impl From<f16> for f32 {
60 #[inline]
61 fn from(x: f16) -> f32 {
62 x.to_f32()
63 }
64}
65
66impl WrappingAdd for f16 {
68 type Output = Self;
69
70 fn wrapping_add(self, x: Self) -> Self {
71 f16::from_f32(self.to_f32() + x.to_f32())
72 }
73}
74
75impl Elem for f16 {
76 fn one() -> Self {
77 f16(0x3C00)
79 }
80}
81
82pub fn f16_to_f32(i: u16) -> f32 {
87 if i & 0x7FFFu16 == 0 {
89 return f32::from_bits((i as u32) << 16);
90 }
91
92 let half_sign = (i & 0x8000u16) as u32;
93 let half_exp = (i & 0x7C00u16) as u32;
94 let half_man = (i & 0x03FFu16) as u32;
95
96 if half_exp == 0x7C00u32 {
98 if half_man == 0 {
100 return f32::from_bits((half_sign << 16) | 0x7F80_0000u32);
101 } else {
102 return f32::from_bits((half_sign << 16) | 0x7FC0_0000u32 | (half_man << 13));
104 }
105 }
106
107 let sign = half_sign << 16;
109 let unbiased_exp = ((half_exp as i32) >> 10) - 15;
111
112 if half_exp == 0 {
114 let e = (half_man as u16).leading_zeros() - 6;
116
117 let exp = (127 - 15 - e) << 23;
119 let man = (half_man << (14 + e)) & 0x7F_FF_FFu32;
120 return f32::from_bits(sign | exp | man);
121 }
122
123 let exp = ((unbiased_exp + 127) as u32) << 23;
125 let man = (half_man & 0x03FFu32) << 13;
126 f32::from_bits(sign | exp | man)
127}
128
129pub fn f32_to_f16(value: f32) -> u16 {
134 let x: u32 = value.to_bits();
135
136 let sign = x & 0x8000_0000u32;
138 let exp = x & 0x7F80_0000u32;
139 let man = x & 0x007F_FFFFu32;
140
141 if exp == 0x7F80_0000u32 {
143 let nan_bit = if man == 0 { 0 } else { 0x0200u32 };
145 return ((sign >> 16) | 0x7C00u32 | nan_bit | (man >> 13)) as u16;
146 }
147
148 let half_sign = sign >> 16;
150 let unbiased_exp = ((exp >> 23) as i32) - 127;
152 let half_exp = unbiased_exp + 15;
153
154 if half_exp >= 0x1F {
156 return (half_sign | 0x7C00u32) as u16;
157 }
158
159 if half_exp <= 0 {
161 if 14 - half_exp > 24 {
163 return half_sign as u16;
165 }
166 let man = man | 0x0080_0000u32;
168 let mut half_man = man >> (14 - half_exp);
169 let round_bit = 1 << (13 - half_exp);
171 if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 {
172 half_man += 1;
173 }
174 return (half_sign | half_man) as u16;
176 }
177
178 let half_exp = (half_exp as u32) << 10;
180 let half_man = man >> 13;
181 let round_bit = 0x0000_1000u32;
183 if (man & round_bit) != 0 && (man & (3 * round_bit - 1)) != 0 {
184 ((half_sign | half_exp | half_man) + 1) as u16
186 } else {
187 (half_sign | half_exp | half_man) as u16
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::{f16, f16_to_f32, f32_to_f16};
194
195 #[test]
196 fn test_known_values() {
197 let cases = [
199 (0.0f32, 0x0000u16),
200 (-0.0, 0x8000),
201 (1.0, 0x3C00),
202 (-1.0, 0xBC00),
203 (2.0, 0x4000),
204 (0.5, 0x3800),
205 (-2.0, 0xC000),
206 (65504.0, 0x7BFF), (f32::INFINITY, 0x7C00),
208 (f32::NEG_INFINITY, 0xFC00),
209 ];
210
211 for (f, bits) in cases {
212 assert_eq!(f32_to_f16(f), bits, "f32_to_f16({f})");
213 assert_eq!(f16_to_f32(bits), f, "f16_to_f32({bits:#06x})");
214 }
215 }
216
217 #[test]
218 fn test_overflow_to_inf() {
219 assert_eq!(f32_to_f16(1e30), 0x7C00);
220 assert_eq!(f32_to_f16(-1e30), 0xFC00);
221 }
222
223 #[test]
224 fn test_nan() {
225 let nan = f32_to_f16(f32::NAN);
226 assert!(f16_to_f32(nan).is_nan());
227 }
228
229 #[test]
230 fn test_subnormal() {
231 let smallest = 2f32.powi(-24);
233 assert_eq!(f32_to_f16(smallest), 0x0001);
234 assert_eq!(f16_to_f32(0x0001), smallest);
235
236 assert_eq!(f32_to_f16(2f32.powi(-26)), 0x0000);
238 }
239
240 #[test]
241 fn test_round_to_even() {
242 assert_eq!(f32_to_f16(2f32.powi(-25)), 0x0000);
245 assert_eq!(f32_to_f16(2f32.powi(-25) * 1.001), 0x0001);
247 }
248
249 #[test]
250 fn test_roundtrip_exact() {
251 for bits in 0..=u16::MAX {
253 let exp = (bits >> 10) & 0x1F;
255 let mant = bits & 0x3FF;
256 if exp == 0x1F && mant != 0 {
257 continue;
258 }
259 let f = f16_to_f32(bits);
260 assert_eq!(f32_to_f16(f), bits, "roundtrip {bits:#06x}");
261 }
262 }
263
264 #[test]
265 fn test_f16_wrapper() {
266 assert_eq!(f16::from_f32(1.0).to_bits(), 0x3C00);
267 assert_eq!(f16::from_bits(0x4000).to_f32(), 2.0);
268 assert_eq!(f32::from(f16::from(3.5f32)), 3.5);
269 assert_eq!(format!("{:?}", f16::from_f32(1.5)), "1.5");
270 }
271}