1#[allow(clippy::if_same_then_else)]
32pub static FP8_E4M3_LUT: [f32; 256] = {
33 let mut table = [0.0f32; 256];
34 let mut i: u32 = 0;
35 while i < 256 {
36 let bits = i as u8;
37 let sign = (bits >> 7) & 1;
38 let exp = (bits >> 3) & 0x0F;
39 let mantissa = bits & 0x07;
40
41 let val = if exp == 0 && mantissa == 0 {
42 0.0f32
43 } else if exp == 0x0F && mantissa == 0x07 {
44 0.0f32 } else if exp == 0 {
46 (mantissa as f32) * (0.015625f32 / 8.0)
48 } else {
49 let f32_exp = (exp as u32 + 120) << 23;
53 let f32_mant = (mantissa as u32) << 20;
54 f32::from_bits(f32_exp | f32_mant)
55 };
56
57 table[i as usize] = if sign == 1 { -val } else { val };
58 i += 1;
59 }
60 table
61};
62
63#[inline(always)]
65pub fn fp8_e4m3_to_f32(bits: u8) -> f32 {
66 FP8_E4M3_LUT[bits as usize]
67}
68
69#[inline(always)]
88pub fn f32_to_bf16(val: f32) -> u16 {
89 if std::env::var("ATLAS_DISABLE_RNE").is_ok() {
90 return (val.to_bits() >> 16) as u16;
91 }
92 let bits = val.to_bits();
93 if val.is_nan() {
94 let sign = ((bits >> 16) & 0x8000) as u16;
95 return sign | 0x7FC0;
96 }
97 let lsb = (bits >> 16) & 1;
98 let rounding_bias = 0x7FFFu32 + lsb;
99 (bits.wrapping_add(rounding_bias) >> 16) as u16
100}
101
102#[inline(always)]
105pub fn bf16_bytes_to_f32(bytes: [u8; 2]) -> f32 {
106 let bits = u16::from_le_bytes(bytes);
107 f32::from_bits((bits as u32) << 16)
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn fp8_lut_reference_values() {
116 assert_eq!(fp8_e4m3_to_f32(0x00).to_bits(), 0x0000_0000); assert_eq!(fp8_e4m3_to_f32(0x80).to_bits(), 0x8000_0000); assert_eq!(fp8_e4m3_to_f32(0x38), 1.0); assert_eq!(fp8_e4m3_to_f32(0xB8), -1.0);
120 assert_eq!(fp8_e4m3_to_f32(0x3C), 1.5); assert_eq!(fp8_e4m3_to_f32(0x7E), 448.0); assert_eq!(fp8_e4m3_to_f32(0xFE), -448.0); assert_eq!(fp8_e4m3_to_f32(0x7F).to_bits(), 0x0000_0000); assert_eq!(fp8_e4m3_to_f32(0xFF).to_bits(), 0x8000_0000); let eps = 1e-10;
128 assert!((fp8_e4m3_to_f32(0x01) - 0.001953125).abs() < eps);
129 assert!((fp8_e4m3_to_f32(0x07) - 0.013671875).abs() < eps);
130 }
131
132 #[test]
133 #[allow(clippy::if_same_then_else)]
134 fn fp8_lut_matches_ocp_values_and_atlas_nan_policy_for_all_bytes() {
135 for i in 0u16..256 {
139 let bits = i as u8;
140 let sign = (bits >> 7) & 1;
141 let exp = (bits >> 3) & 0x0F;
142 let mant = bits & 0x07;
143
144 let magnitude = if exp == 0x0F && mant == 0x07 {
145 0.0f32
146 } else if exp == 0 && mant == 0 {
147 0.0f32
148 } else if exp == 0 {
149 (mant as f32 / 8.0) * 2.0f32.powi(-6)
150 } else {
151 (1.0 + mant as f32 / 8.0) * 2.0f32.powi(exp as i32 - 7)
152 };
153 let expected = if sign == 1 { -magnitude } else { magnitude };
154 let actual = fp8_e4m3_to_f32(bits);
155 assert_eq!(
156 actual.to_bits(),
157 expected.to_bits(),
158 "LUT mismatch at {i:#04x}: expected {expected:?}, got {actual:?}"
159 );
160 }
161 }
162
163 #[test]
166 fn f32_to_bf16_is_rne_byte_exact() {
167 fn convert(bits: u32) -> u16 {
168 f32_to_bf16(f32::from_bits(bits))
169 }
170
171 assert_eq!(convert(0x3F80_0800), 0x3F80, "1.0 + below-half-ULP -> 1.0");
173 assert_eq!(
176 convert(0x3F80_8000),
177 0x3F80,
178 "1.0 + exact-half-ULP, LSB=0 -> 1.0 (even)"
179 );
180 assert_eq!(
182 convert(0x3F80_8001),
183 0x3F81,
184 "1.0 + above-half-ULP -> next bf16 (truncation would give 0x3F80)"
185 );
186 assert_eq!(
189 convert(0x3F81_8000),
190 0x3F82,
191 "1.0078125 + exact-half-ULP, LSB=1 -> 1.015625"
192 );
193 assert_eq!(convert(0xBF80_8001), 0xBF81, "negative round up");
195 assert_eq!(convert(0x0000_0000), 0x0000, "+0.0");
197 assert_eq!(convert(0x8000_0000), 0x8000, "-0.0");
198 assert_eq!(convert(0x0000_0001), 0x0000, "tiny subnormal -> 0");
200 assert_eq!(convert(0x7F80_0000), 0x7F80, "+inf");
202 assert_eq!(convert(0xFF80_0000), 0xFF80, "-inf");
203 assert_eq!(
206 convert(0x7F7F_FFFF),
207 0x7F80,
208 "max-finite f32 rounds to +inf bf16"
209 );
210 assert_eq!(convert(0x7FC0_0000), 0x7FC0, "qnan +");
213 assert_eq!(convert(0xFFC0_0000), 0xFFC0, "qnan -");
214 assert_eq!(convert(0x7F80_0001), 0x7FC0, "snan + -> qnan +");
215 }
216
217 #[test]
222 fn f32_to_bf16_matches_pytorch() {
223 let cases: &[(u32, u16, &str)] = &[
224 (0x3F80_0000, 0x3F80, "1.0"),
225 (0x4000_0000, 0x4000, "2.0"),
226 (0xC000_0000, 0xC000, "-2.0"),
227 (0x3FC0_0000, 0x3FC0, "1.5"),
228 (
229 0x3DCC_CCCD,
230 0x3DCD,
231 "0.1 -> RNE rounds UP to 0x3DCD (trunc=0x3DCC)",
232 ),
233 (0x3F4C_CCCD, 0x3F4D, "0.8 -> RNE rounds UP to 0x3F4D"),
234 (0x40C9_0FDB, 0x40C9, "pi -> truncates (next bit < half)"),
235 (0x402D_F854, 0x402E, "e -> RNE rounds UP (next bit > half)"),
236 (0x4490_0000, 0x4490, "1152.0"),
237 (0x3727_C5AC, 0x3728, "1e-5 -> RNE rounds UP"),
238 ];
239 for (f32_bits, want, desc) in cases {
240 let got = f32_to_bf16(f32::from_bits(*f32_bits));
241 assert_eq!(
242 got, *want,
243 "f32={f32_bits:#010x} ({desc}): want bf16={want:#06x}, got {got:#06x}"
244 );
245 }
246 }
247
248 #[test]
249 fn disable_rne_presence_uses_truncation() {
250 const THIS_TEST: &str = "numeric::tests::disable_rne_presence_uses_truncation";
251 const CHILD_MARKER: &str = "ATLAS_NUMERIC_RNE_CHILD";
252
253 if std::env::var_os(CHILD_MARKER).is_some() {
254 assert_eq!(
255 f32_to_bf16(f32::from_bits(0x3F80_8001)),
256 0x3F80,
257 "the escape hatch must truncate an above-half-ULP value"
258 );
259 return;
260 }
261
262 for value in ["0", "1"] {
263 let output = std::process::Command::new(std::env::current_exe().unwrap())
264 .args(["--exact", THIS_TEST])
265 .env(CHILD_MARKER, "1")
266 .env("ATLAS_DISABLE_RNE", value)
267 .output()
268 .unwrap();
269 assert!(
270 output.status.success(),
271 "ATLAS_DISABLE_RNE={value} child failed:\n{}",
272 String::from_utf8_lossy(&output.stdout)
273 );
274 }
275 }
276
277 #[test]
278 fn bf16_widening_is_byte_exact_for_every_pattern() {
279 for bits in 0u32..=0xFFFF {
280 let bf16 = bits as u16;
281 let widened = bf16_bytes_to_f32(bf16.to_le_bytes());
282 assert_eq!(
283 widened.to_bits(),
284 bits << 16,
285 "widening moved bits for bf16 {bf16:#06x}"
286 );
287 }
288 }
289
290 #[test]
291 fn bf16_narrowing_preserves_every_non_nan_bf16_value() {
292 for bits in 0u32..=0xFFFF {
293 let bf16 = bits as u16;
294 let widened = f32::from_bits(bits << 16);
295 if widened.is_nan() {
296 continue;
297 }
298 assert_eq!(
299 f32_to_bf16(widened),
300 bf16,
301 "round trip failed for bf16 {bf16:#06x}"
302 );
303 }
304 }
305}