1#![no_std]
51#![cfg_attr(feature = "f16", feature(f16))]
52#![cfg_attr(feature = "f128", feature(f128))]
53#![cfg_attr(feature = "simd", feature(portable_simd))]
54
55#[cfg(test)]
56extern crate std;
57
58mod convert;
59mod dispatch;
60mod pow;
61#[cfg(feature = "simd")]
62pub mod simd;
63mod uf16;
64mod uf32;
65#[cfg(feature = "f128")]
66mod uf64;
67mod uf8;
68
69pub use convert::ConversionError;
70pub use pow::{Pow1mUf, PowUf};
71pub use uf8::{Uf8, Uf8E4M4, Uf8E5M3};
72pub use uf16::{Uf16, Uf16E5M11, Uf16E6M10};
73pub use uf32::{Uf32, Uf32E8M24};
74#[cfg(feature = "f128")]
75pub use uf64::{Uf64, Uf64E11M52};
76
77#[cfg(test)]
78mod tests {
79 #[cfg(feature = "f128")]
80 use super::Uf64;
81 use super::{ConversionError, Pow1mUf, PowUf, Uf8, Uf8E5M3, Uf16, Uf16E6M10, Uf32};
82
83 #[test]
84 fn canonical_one_bits_match_the_layouts() {
85 assert_eq!(Uf8::ONE.to_bits(), 0x70);
86 assert_eq!(Uf8E5M3::ONE.to_bits(), 0x78);
87 assert_eq!(Uf16::ONE.to_bits(), 0x7800);
88 assert_eq!(Uf16E6M10::ONE.to_bits(), 0x7c00);
89 assert_eq!(Uf32::ONE.to_bits(), 0x7f00_0000);
90 #[cfg(feature = "f128")]
91 assert_eq!(Uf64::ONE.to_bits(), 0x3ff0_0000_0000_0000);
92 }
93
94 #[test]
95 fn uf8_finite_values_round_trip_through_f32() {
96 for bits in u8::MIN..=u8::MAX {
97 let value = Uf8::from_bits(bits);
98
99 if value.is_nan() {
100 continue;
101 }
102
103 assert_eq!(Uf8::from_f32(value.to_f32()).to_bits(), bits);
104 }
105 }
106
107 #[test]
108 fn uf8_e5m3_finite_values_round_trip_through_f32() {
109 for bits in u8::MIN..=u8::MAX {
110 let value = Uf8E5M3::from_bits(bits);
111
112 if value.is_nan() {
113 continue;
114 }
115
116 assert_eq!(Uf8E5M3::from_f32(value.to_f32()).to_bits(), bits);
117 }
118 }
119
120 #[test]
121 fn conversions_handle_special_values() {
122 assert!(Uf8::from_f32(f32::NAN).is_nan());
123 assert!(Uf8E5M3::from_f32(f32::NAN).is_nan());
124 assert!(Uf16::from_f32(f32::NEG_INFINITY).is_nan());
125 assert!(Uf16E6M10::from_f32(f32::NEG_INFINITY).is_nan());
126 assert!(Uf32::from_f64(-1.0).is_nan());
127 #[cfg(feature = "f128")]
128 assert!(Uf64::from_f64(-1.0).is_nan());
129
130 assert!(Uf8::from_f32(f32::INFINITY).is_infinite());
131 assert!(Uf8E5M3::from_f32(f32::INFINITY).is_infinite());
132 assert!(Uf16::from_f32(f32::INFINITY).is_infinite());
133 assert!(Uf16E6M10::from_f32(f32::INFINITY).is_infinite());
134 assert!(Uf32::from_f64(f64::INFINITY).is_infinite());
135 #[cfg(feature = "f128")]
136 assert!(Uf64::from_f64(f64::INFINITY).is_infinite());
137 }
138
139 #[test]
140 fn try_from_f64_rejects_invalid_or_unrepresentable_values() {
141 assert_eq!(Uf8::try_from(-1.0_f64), Err(ConversionError::Negative));
142 assert_eq!(Uf16::try_from(f64::NAN), Err(ConversionError::Nan));
143 assert_eq!(
144 Uf32::try_from(f64::INFINITY),
145 Err(ConversionError::Infinite)
146 );
147
148 assert_eq!(Uf8::try_from(1.0e20_f64), Err(ConversionError::Overflow));
149 assert_eq!(Uf16::try_from(1.0e20_f64), Err(ConversionError::Overflow));
150 assert_eq!(Uf8::try_from(1.0e-20_f64), Err(ConversionError::Underflow));
151
152 assert_eq!(Uf8::try_from(2.0_f64), Ok(Uf8::from_f32(2.0)));
153 assert_eq!(Uf8E5M3::try_from(2.0_f64), Ok(Uf8E5M3::from_f32(2.0)));
154 assert_eq!(Uf16::try_from(2.0_f64), Ok(Uf16::from_f32(2.0)));
155 assert_eq!(Uf16E6M10::try_from(2.0_f64), Ok(Uf16E6M10::from_f32(2.0)));
156 assert_eq!(Uf32::try_from(2.0_f64), Ok(Uf32::from_f64(2.0)));
157 #[cfg(feature = "f128")]
158 assert_eq!(Uf64::try_from_f64(2.0_f64), Ok(Uf64::from_f64(2.0)));
159 }
160
161 #[test]
162 fn try_from_integer_types() {
163 assert_eq!(Uf8::try_from(2_u8), Ok(Uf8::from_f32(2.0)));
164 assert_eq!(Uf8E5M3::try_from(2_u8), Ok(Uf8E5M3::from_f32(2.0)));
165 assert_eq!(Uf16::try_from(1024_u32), Ok(Uf16::from_f32(1024.0)));
166 assert_eq!(
167 Uf16E6M10::try_from(1024_u32),
168 Ok(Uf16E6M10::from_f32(1024.0))
169 );
170 assert_eq!(Uf32::try_from(1024_u64), Ok(Uf32::from_f64(1024.0)));
171 #[cfg(feature = "f128")]
172 assert_eq!(Uf64::try_from(1024_u64), Ok(Uf64::from_f64(1024.0)));
173
174 assert_eq!(Uf8::try_from(-1_i8), Err(ConversionError::Negative));
175 assert_eq!(Uf8::try_from(u128::MAX), Err(ConversionError::Overflow));
176 }
177
178 #[cfg(feature = "f16")]
179 #[test]
180 fn f16_conversions_are_available_when_enabled() {
181 let native = 2.0_f16;
182
183 assert_eq!(Uf8::from_f16(native).to_f16(), native);
184 assert_eq!(Uf8E5M3::from_f16(native).to_f16(), native);
185 assert_eq!(Uf16::from_f16(native).to_f16(), native);
186 assert_eq!(Uf16E6M10::from_f16(native).to_f16(), native);
187 assert_eq!(Uf32::from_f16(native).to_f16(), native);
188 #[cfg(feature = "f128")]
189 assert_eq!(Uf64::from_f16(native).to_f16(), native);
190
191 assert_eq!(Uf8::from(native), Uf8::from_f16(native));
192 assert_eq!(Uf8E5M3::from(native), Uf8E5M3::from_f16(native));
193 assert_eq!(Uf16::from(native), Uf16::from_f16(native));
194 assert_eq!(Uf16E6M10::from(native), Uf16E6M10::from_f16(native));
195 assert_eq!(Uf32::from(native), Uf32::from_f16(native));
196 #[cfg(feature = "f128")]
197 assert_eq!(Uf64::from(native), Uf64::from_f16(native));
198
199 let _: f16 = Uf8::from_f16(native).into();
200 let _: f16 = Uf8E5M3::from_f16(native).into();
201 let _: f16 = Uf16::from_f16(native).into();
202 let _: f16 = Uf16E6M10::from_f16(native).into();
203 let _: f16 = Uf32::from_f16(native).into();
204 #[cfg(feature = "f128")]
205 let _: f16 = Uf64::from_f16(native).into();
206 }
207
208 #[test]
209 fn subnormal_values_decode_correctly() {
210 assert_eq!(Uf8::MIN_POSITIVE.to_f32(), 2.0_f32.powi(-10));
211 assert_eq!(Uf8E5M3::MIN_POSITIVE.to_f32(), 2.0_f32.powi(-17));
212 assert_eq!(Uf16::MIN_POSITIVE.to_f32(), 2.0_f32.powi(-25));
213 assert_eq!(Uf16E6M10::MIN_POSITIVE.to_f32(), 2.0_f32.powi(-40));
214 assert_eq!(Uf32::MIN_POSITIVE.to_f64(), 2.0_f64.powi(-150));
215 #[cfg(feature = "f128")]
216 assert_eq!(
217 Uf64::MIN_POSITIVE.to_f64(),
218 f64::MIN_POSITIVE / 2.0_f64.powi(52)
219 );
220 }
221
222 #[test]
223 fn arithmetic_promotes_computes_and_demotes() {
224 assert_eq!((Uf8::from_f32(1.0) + Uf8::from_f32(1.0)).to_f32(), 2.0);
225 assert_eq!(
226 (Uf8E5M3::from_f32(1.0) + Uf8E5M3::from_f32(1.0)).to_f32(),
227 2.0
228 );
229 assert_eq!((Uf16::from_f32(3.0) * Uf16::from_f32(0.5)).to_f32(), 1.5);
230 assert_eq!(
231 (Uf16E6M10::from_f32(3.0) * Uf16E6M10::from_f32(0.5)).to_f32(),
232 1.5
233 );
234 assert_eq!((Uf32::from_f64(9.0) / Uf32::from_f64(3.0)).to_f64(), 3.0);
235 #[cfg(feature = "f128")]
236 assert_eq!((Uf64::from_f64(9.0) / Uf64::from_f64(3.0)).to_f64(), 3.0);
237 }
238
239 #[test]
240 fn native_float_bases_can_use_unsigned_float_exponents() {
241 assert_eq!(9.0_f32.powuf(Uf8::from_f32(0.5)), 3.0);
242 assert_eq!(9.0_f32.powuf(Uf8E5M3::from_f32(0.5)), 3.0);
243 assert_eq!(9.0_f32.powuf(Uf16::from_f32(0.5)), 3.0);
244 assert_eq!(9.0_f64.powuf(Uf16E6M10::from_f32(0.5)), 3.0);
245 assert_eq!(9.0_f64.powuf(Uf32::from_f64(0.5)), 3.0);
246 assert_eq!(2.0_f32.powuf(Uf16::from_f32(8.0)), 256.0);
247 assert_eq!((-2.0_f32).powuf(Uf8::from_f32(3.0)), -8.0);
248 assert_eq!(f32::NAN.powuf(Uf8::ZERO), 1.0);
249 assert!((16.0_f64.powuf(Uf32::from_f64(1.25)) - 32.0).abs() < 1.0e-12);
250
251 #[cfg(feature = "f128")]
252 {
253 assert_eq!(9.0_f64.powuf(Uf64::from_f64(0.5)), 3.0);
254 assert_eq!(2.0_f64.powuf(Uf64::from_f64(10.0)), 1024.0);
255 }
256 }
257
258 #[test]
259 fn native_float_bases_can_use_complement_exponents() {
260 assert_eq!(0.75_f32.pow1muf(Uf8::from_f32(0.5)), 0.5);
261 assert_eq!(0.75_f32.pow1muf(Uf16::from_f32(0.5)), 0.5);
262 assert_eq!(0.5_f64.pow1muf(Uf32::from_f64(2.0)), 0.25);
263 assert_eq!(f32::NAN.pow1muf(Uf8::ZERO), 1.0);
264 assert!(
265 (0.25_f64.pow1muf(Uf16E6M10::from_f32(1.25)) - 0.697_953_644_326_574_7).abs() < 1.0e-15
266 );
267
268 #[cfg(feature = "f128")]
269 {
270 assert_eq!(0.5_f64.pow1muf(Uf64::from_f64(2.0)), 0.25);
271 }
272 }
273
274 #[test]
275 fn uf8_pow_lut_matches_promoted_arithmetic() {
276 assert_eq!(Uf8::from_f32(9.0).powuf(Uf8::from_f32(0.5)).to_f32(), 3.0);
277
278 for a_bits in u8::MIN..=u8::MAX {
279 for b_bits in u8::MIN..=u8::MAX {
280 let a = Uf8::from_bits(a_bits);
281 let b = Uf8::from_bits(b_bits);
282
283 assert_eq!(
284 a.powuf(b).to_bits(),
285 Uf8::from_f32(a.to_f32().powuf(b)).to_bits()
286 );
287 }
288 }
289 }
290
291 #[test]
292 fn uf8_e5m3_pow_lut_matches_promoted_arithmetic() {
293 assert_eq!(
294 Uf8E5M3::from_f32(9.0)
295 .powuf(Uf8E5M3::from_f32(0.5))
296 .to_f32(),
297 3.0
298 );
299
300 for a_bits in u8::MIN..=u8::MAX {
301 for b_bits in u8::MIN..=u8::MAX {
302 let a = Uf8E5M3::from_bits(a_bits);
303 let b = Uf8E5M3::from_bits(b_bits);
304
305 assert_eq!(
306 a.powuf(b).to_bits(),
307 Uf8E5M3::from_f32(a.to_f32().powuf(b)).to_bits()
308 );
309 }
310 }
311 }
312
313 #[test]
314 fn uf8_cross_layout_pow_luts_match_promoted_arithmetic() {
315 assert_eq!(
316 Uf8::from_f32(9.0).powuf(Uf8E5M3::from_f32(0.5)).to_f32(),
317 3.0
318 );
319 assert_eq!(
320 Uf8E5M3::from_f32(9.0).powuf(Uf8::from_f32(0.5)).to_f32(),
321 3.0
322 );
323
324 for a_bits in u8::MIN..=u8::MAX {
325 for b_bits in u8::MIN..=u8::MAX {
326 let e4m4 = Uf8::from_bits(a_bits);
327 let e5m3 = Uf8E5M3::from_bits(b_bits);
328
329 assert_eq!(
330 e4m4.powuf(e5m3).to_bits(),
331 Uf8::from_f32(e4m4.to_f32().powuf(e5m3)).to_bits()
332 );
333 assert_eq!(
334 e5m3.powuf(e4m4).to_bits(),
335 Uf8E5M3::from_f32(e5m3.to_f32().powuf(e4m4)).to_bits()
336 );
337 }
338 }
339 }
340
341 #[test]
342 fn uf8_pow1m_lut_matches_promoted_arithmetic() {
343 assert_eq!(
344 Uf8::from_f32(0.75).pow1muf(Uf8::from_f32(0.5)).to_f32(),
345 0.5
346 );
347
348 for a_bits in u8::MIN..=u8::MAX {
349 for b_bits in u8::MIN..=u8::MAX {
350 let u = Uf8::from_bits(a_bits);
351 let exponent = Uf8::from_bits(b_bits);
352
353 assert_eq!(
354 u.pow1muf(exponent).to_bits(),
355 Uf8::from_f32(u.to_f32().pow1muf(exponent)).to_bits()
356 );
357 }
358 }
359 }
360
361 #[test]
362 fn uf8_e5m3_pow1m_lut_matches_promoted_arithmetic() {
363 assert_eq!(
364 Uf8E5M3::from_f32(0.75)
365 .pow1muf(Uf8E5M3::from_f32(0.5))
366 .to_f32(),
367 0.5
368 );
369
370 for a_bits in u8::MIN..=u8::MAX {
371 for b_bits in u8::MIN..=u8::MAX {
372 let u = Uf8E5M3::from_bits(a_bits);
373 let exponent = Uf8E5M3::from_bits(b_bits);
374
375 assert_eq!(
376 u.pow1muf(exponent).to_bits(),
377 Uf8E5M3::from_f32(u.to_f32().pow1muf(exponent)).to_bits()
378 );
379 }
380 }
381 }
382
383 #[test]
384 fn uf8_cross_layout_pow1m_luts_match_promoted_arithmetic() {
385 assert_eq!(
386 Uf8::from_f32(0.75).pow1muf(Uf8E5M3::from_f32(0.5)).to_f32(),
387 0.5
388 );
389 assert_eq!(
390 Uf8E5M3::from_f32(0.75).pow1muf(Uf8::from_f32(0.5)).to_f32(),
391 0.5
392 );
393
394 for a_bits in u8::MIN..=u8::MAX {
395 for b_bits in u8::MIN..=u8::MAX {
396 let e4m4 = Uf8::from_bits(a_bits);
397 let e5m3 = Uf8E5M3::from_bits(b_bits);
398
399 assert_eq!(
400 e4m4.pow1muf(e5m3).to_bits(),
401 Uf8::from_f32(e4m4.to_f32().pow1muf(e5m3)).to_bits()
402 );
403 assert_eq!(
404 e5m3.pow1muf(e4m4).to_bits(),
405 Uf8E5M3::from_f32(e5m3.to_f32().pow1muf(e4m4)).to_bits()
406 );
407 }
408 }
409 }
410
411 #[cfg(any(not(feature = "f16"), feature = "soft-float"))]
412 #[test]
413 fn uf8_lut_matches_promoted_arithmetic() {
414 for a_bits in u8::MIN..=u8::MAX {
415 for b_bits in u8::MIN..=u8::MAX {
416 let a = Uf8::from_bits(a_bits);
417 let b = Uf8::from_bits(b_bits);
418 let a_f32 = a.to_f32();
419 let b_f32 = b.to_f32();
420
421 assert_eq!((a + b).to_bits(), Uf8::from_f32(a_f32 + b_f32).to_bits());
422 assert_eq!((a - b).to_bits(), Uf8::from_f32(a_f32 - b_f32).to_bits());
423 assert_eq!((a * b).to_bits(), Uf8::from_f32(a_f32 * b_f32).to_bits());
424 assert_eq!((a / b).to_bits(), Uf8::from_f32(a_f32 / b_f32).to_bits());
425 }
426 }
427 }
428
429 #[cfg(any(not(feature = "f16"), feature = "soft-float"))]
430 #[test]
431 fn uf8_e5m3_lut_matches_promoted_arithmetic() {
432 for a_bits in u8::MIN..=u8::MAX {
433 for b_bits in u8::MIN..=u8::MAX {
434 let a = Uf8E5M3::from_bits(a_bits);
435 let b = Uf8E5M3::from_bits(b_bits);
436 let a_f32 = a.to_f32();
437 let b_f32 = b.to_f32();
438
439 assert_eq!(
440 (a + b).to_bits(),
441 Uf8E5M3::from_f32(a_f32 + b_f32).to_bits()
442 );
443 assert_eq!(
444 (a - b).to_bits(),
445 Uf8E5M3::from_f32(a_f32 - b_f32).to_bits()
446 );
447 assert_eq!(
448 (a * b).to_bits(),
449 Uf8E5M3::from_f32(a_f32 * b_f32).to_bits()
450 );
451 assert_eq!(
452 (a / b).to_bits(),
453 Uf8E5M3::from_f32(a_f32 / b_f32).to_bits()
454 );
455 }
456 }
457 }
458
459 #[test]
460 fn negative_subtraction_result_is_nan() {
461 assert!((Uf8::from_f32(1.0) - Uf8::from_f32(2.0)).is_nan());
462 assert!((Uf8E5M3::from_f32(1.0) - Uf8E5M3::from_f32(2.0)).is_nan());
463 assert!((Uf16::from_f32(1.0) - Uf16::from_f32(2.0)).is_nan());
464 assert!((Uf16E6M10::from_f32(1.0) - Uf16E6M10::from_f32(2.0)).is_nan());
465 assert!((Uf32::from_f64(1.0) - Uf32::from_f64(2.0)).is_nan());
466 #[cfg(feature = "f128")]
467 assert!((Uf64::from_f64(1.0) - Uf64::from_f64(2.0)).is_nan());
468 }
469
470 #[test]
471 fn raw_bits_define_total_ordering() {
472 assert!(Uf8::ZERO < Uf8::MIN_POSITIVE);
473 assert!(Uf8::MAX < Uf8::INFINITY);
474 assert!(Uf8::INFINITY < Uf8::NAN);
475
476 assert!(Uf8E5M3::ZERO < Uf8E5M3::MIN_POSITIVE);
477 assert!(Uf8E5M3::MAX < Uf8E5M3::INFINITY);
478 assert!(Uf8E5M3::INFINITY < Uf8E5M3::NAN);
479
480 assert!(Uf16::ZERO < Uf16::MIN_POSITIVE);
481 assert!(Uf16::MAX < Uf16::INFINITY);
482 assert!(Uf16::INFINITY < Uf16::NAN);
483
484 assert!(Uf16E6M10::ZERO < Uf16E6M10::MIN_POSITIVE);
485 assert!(Uf16E6M10::MAX < Uf16E6M10::INFINITY);
486 assert!(Uf16E6M10::INFINITY < Uf16E6M10::NAN);
487
488 assert!(Uf32::ZERO < Uf32::MIN_POSITIVE);
489 assert!(Uf32::MAX < Uf32::INFINITY);
490 assert!(Uf32::INFINITY < Uf32::NAN);
491
492 #[cfg(feature = "f128")]
493 {
494 assert!(Uf64::ZERO < Uf64::MIN_POSITIVE);
495 assert!(Uf64::MAX < Uf64::INFINITY);
496 assert!(Uf64::INFINITY < Uf64::NAN);
497 }
498 }
499
500 #[test]
501 fn formatting_delegates_to_promoted_float() {
502 macro_rules! assert_f32_formatting {
503 ($value:expr, $native:expr) => {
504 assert_eq!(std::format!("{}", $value), std::format!("{}", $native));
505 assert_eq!(
506 std::format!("{:.2}", $value),
507 std::format!("{:.2}", $native)
508 );
509 assert_eq!(
510 std::format!("{:08.2}", $value),
511 std::format!("{:08.2}", $native)
512 );
513 assert_eq!(std::format!("{:e}", $value), std::format!("{:e}", $native));
514 assert_eq!(std::format!("{:E}", $value), std::format!("{:E}", $native));
515 };
516 }
517
518 macro_rules! assert_f64_formatting {
519 ($value:expr, $native:expr) => {
520 assert_eq!(std::format!("{}", $value), std::format!("{}", $native));
521 assert_eq!(
522 std::format!("{:.4}", $value),
523 std::format!("{:.4}", $native)
524 );
525 assert_eq!(
526 std::format!("{:010.4}", $value),
527 std::format!("{:010.4}", $native)
528 );
529 assert_eq!(std::format!("{:e}", $value), std::format!("{:e}", $native));
530 assert_eq!(std::format!("{:E}", $value), std::format!("{:E}", $native));
531 };
532 }
533
534 let uf8 = Uf8::from_f32(1.5);
535 assert_f32_formatting!(uf8, uf8.to_f32());
536
537 let uf8_e5m3 = Uf8E5M3::from_f32(1.5);
538 assert_f32_formatting!(uf8_e5m3, uf8_e5m3.to_f32());
539
540 let uf16 = Uf16::from_f32(1.5);
541 assert_f32_formatting!(uf16, uf16.to_f32());
542
543 let uf16_e6m10 = Uf16E6M10::from_f32(1.5);
544 assert_f32_formatting!(uf16_e6m10, uf16_e6m10.to_f32());
545
546 let uf32 = Uf32::from_f64(1.5);
547 assert_f64_formatting!(uf32, uf32.to_f64());
548
549 #[cfg(feature = "f128")]
550 {
551 let uf64 = Uf64::from_f64(1.5);
552 assert_f64_formatting!(uf64, uf64.to_f64());
553 }
554 }
555
556 #[test]
557 fn round_to_nearest_even_when_encoding() {
558 assert_eq!(Uf8::from_f32(1.0 + 1.0 / 32.0).to_bits(), 0x70);
559 assert_eq!(Uf8::from_f32(1.0 + 3.0 / 32.0).to_bits(), 0x72);
560
561 assert_eq!(
562 Uf32::from_f64(1.0 + 2.0_f64.powi(-25)).to_bits(),
563 Uf32::ONE.to_bits()
564 );
565 assert_eq!(
566 Uf32::from_f64(1.0 + 3.0 * 2.0_f64.powi(-25)).to_bits(),
567 Uf32::ONE.to_bits() + 2
568 );
569 }
570}