Add classify
and related methods for f16
and f128
· model-checking/verify-rust-std@f710e38 (original) (raw)
`@@ -13,6 +13,7 @@
`
13
13
``
14
14
`use crate::convert::FloatToInt;
`
15
15
`use crate::mem;
`
``
16
`+
use crate::num::FpCategory;
`
16
17
``
17
18
`/// Basic mathematical constants.
`
18
19
`#[unstable(feature = "f16", issue = "116909")]
`
`@@ -244,7 +245,13 @@ impl f16 {
`
244
245
``
245
246
`/// Sign bit
`
246
247
`#[cfg(not(bootstrap))]
`
247
``
`-
const SIGN_MASK: u16 = 0x8000;
`
``
248
`+
pub(crate) const SIGN_MASK: u16 = 0x8000;
`
``
249
+
``
250
`+
/// Exponent mask
`
``
251
`+
pub(crate) const EXP_MASK: u16 = 0x7c00;
`
``
252
+
``
253
`+
/// Mantissa mask
`
``
254
`+
pub(crate) const MAN_MASK: u16 = 0x03ff;
`
248
255
``
249
256
`/// Minimum representable positive value (min subnormal)
`
250
257
`#[cfg(not(bootstrap))]
`
`@@ -344,6 +351,159 @@ impl f16 {
`
344
351
`self.abs_private() < Self::INFINITY
`
345
352
`}
`
346
353
``
``
354
`` +
/// Returns true
if the number is [subnormal].
``
``
355
`+
///
`
``
356
/// ```
``
357
`+
/// #![feature(f16)]
`
``
358
`+
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
`
``
359
`+
///
`
``
360
`+
/// let min = f16::MIN_POSITIVE; // 6.1035e-5
`
``
361
`+
/// let max = f16::MAX;
`
``
362
`+
/// let lower_than_min = 1.0e-7_f16;
`
``
363
`+
/// let zero = 0.0_f16;
`
``
364
`+
///
`
``
365
`+
/// assert!(!min.is_subnormal());
`
``
366
`+
/// assert!(!max.is_subnormal());
`
``
367
`+
///
`
``
368
`+
/// assert!(!zero.is_subnormal());
`
``
369
`+
/// assert!(!f16::NAN.is_subnormal());
`
``
370
`+
/// assert!(!f16::INFINITY.is_subnormal());
`
``
371
`` +
/// // Values between 0
and min
are Subnormal.
``
``
372
`+
/// assert!(lower_than_min.is_subnormal());
`
``
373
`+
/// # }
`
``
374
/// ```
``
375
`+
/// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number
`
``
376
`+
#[inline]
`
``
377
`+
#[must_use]
`
``
378
`+
#[cfg(not(bootstrap))]
`
``
379
`+
#[unstable(feature = "f16", issue = "116909")]
`
``
380
`+
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
`
``
381
`+
pub const fn is_subnormal(self) -> bool {
`
``
382
`+
matches!(self.classify(), FpCategory::Subnormal)
`
``
383
`+
}
`
``
384
+
``
385
`` +
/// Returns true
if the number is neither zero, infinite, [subnormal], or NaN.
``
``
386
`+
///
`
``
387
/// ```
``
388
`+
/// #![feature(f16)]
`
``
389
`+
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
`
``
390
`+
///
`
``
391
`+
/// let min = f16::MIN_POSITIVE; // 6.1035e-5
`
``
392
`+
/// let max = f16::MAX;
`
``
393
`+
/// let lower_than_min = 1.0e-7_f16;
`
``
394
`+
/// let zero = 0.0_f16;
`
``
395
`+
///
`
``
396
`+
/// assert!(min.is_normal());
`
``
397
`+
/// assert!(max.is_normal());
`
``
398
`+
///
`
``
399
`+
/// assert!(!zero.is_normal());
`
``
400
`+
/// assert!(!f16::NAN.is_normal());
`
``
401
`+
/// assert!(!f16::INFINITY.is_normal());
`
``
402
`` +
/// // Values between 0
and min
are Subnormal.
``
``
403
`+
/// assert!(!lower_than_min.is_normal());
`
``
404
`+
/// # }
`
``
405
/// ```
``
406
`+
/// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number
`
``
407
`+
#[inline]
`
``
408
`+
#[must_use]
`
``
409
`+
#[cfg(not(bootstrap))]
`
``
410
`+
#[unstable(feature = "f16", issue = "116909")]
`
``
411
`+
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
`
``
412
`+
pub const fn is_normal(self) -> bool {
`
``
413
`+
matches!(self.classify(), FpCategory::Normal)
`
``
414
`+
}
`
``
415
+
``
416
`+
/// Returns the floating point category of the number. If only one property
`
``
417
`+
/// is going to be tested, it is generally faster to use the specific
`
``
418
`+
/// predicate instead.
`
``
419
`+
///
`
``
420
/// ```
``
421
`+
/// #![feature(f16)]
`
``
422
`+
/// # #[cfg(target_arch = "aarch64")] { // FIXME(f16_F128): rust-lang/rust#123885
`
``
423
`+
///
`
``
424
`+
/// use std::num::FpCategory;
`
``
425
`+
///
`
``
426
`+
/// let num = 12.4_f16;
`
``
427
`+
/// let inf = f16::INFINITY;
`
``
428
`+
///
`
``
429
`+
/// assert_eq!(num.classify(), FpCategory::Normal);
`
``
430
`+
/// assert_eq!(inf.classify(), FpCategory::Infinite);
`
``
431
`+
/// # }
`
``
432
/// ```
``
433
`+
#[inline]
`
``
434
`+
#[cfg(not(bootstrap))]
`
``
435
`+
#[unstable(feature = "f16", issue = "116909")]
`
``
436
`+
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
`
``
437
`+
pub const fn classify(self) -> FpCategory {
`
``
438
`+
// A previous implementation for f32/f64 tried to only use bitmask-based checks,
`
``
439
`` +
// using to_bits
to transmute the float to its bit repr and match on that.
``
``
440
`+
// Unfortunately, floating point numbers can be much worse than that.
`
``
441
`` +
// This also needs to not result in recursive evaluations of to_bits
.
``
``
442
`+
//
`
``
443
+
``
444
`` +
// Platforms without native support generally convert to f32
to perform operations,
``
``
445
`` +
// and most of these platforms correctly round back to f16
after each operation.
``
``
446
`` +
// However, some platforms have bugs where they keep the excess f32
precision (e.g.
``
``
447
`+
// WASM, see llvm/llvm-project#96437). This implementation makes a best-effort attempt
`
``
448
`+
// to account for that excess precision.
`
``
449
`+
if self.is_infinite() {
`
``
450
`+
// Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
`
``
451
`+
FpCategory::Infinite
`
``
452
`+
} else if self.is_nan() {
`
``
453
`+
// And it may not be NaN, as it can simply be an "overextended" finite value.
`
``
454
`+
FpCategory::Nan
`
``
455
`+
} else {
`
``
456
`+
// However, std can't simply compare to zero to check for zero, either,
`
``
457
`+
// as correctness requires avoiding equality tests that may be Subnormal == -0.0
`
``
458
`+
// because it may be wrong under "denormals are zero" and "flush to zero" modes.
`
``
459
`+
// Most of std's targets don't use those, but they are used for thumbv7neon.
`
``
460
`+
// So, this does use bitpattern matching for the rest.
`
``
461
+
``
462
`+
// SAFETY: f16 to u16 is fine. Usually.
`
``
463
`+
// If classify has gotten this far, the value is definitely in one of these categories.
`
``
464
`+
unsafe { f16::partial_classify(self) }
`
``
465
`+
}
`
``
466
`+
}
`
``
467
+
``
468
`+
/// This doesn't actually return a right answer for NaN on purpose,
`
``
469
`+
/// seeing as how it cannot correctly discern between a floating point NaN,
`
``
470
`+
/// and some normal floating point numbers truncated from an x87 FPU.
`
``
471
`+
///
`
``
472
`+
/// # Safety
`
``
473
`+
///
`
``
474
`+
/// This requires making sure you call this function for values it answers correctly on,
`
``
475
`+
/// otherwise it returns a wrong answer. This is not important for memory safety per se,
`
``
476
`+
/// but getting floats correct is important for not accidentally leaking const eval
`
``
477
`+
/// runtime-deviating logic which may or may not be acceptable.
`
``
478
`+
#[inline]
`
``
479
`+
#[cfg(not(bootstrap))]
`
``
480
`+
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
`
``
481
`+
const unsafe fn partial_classify(self) -> FpCategory {
`
``
482
`+
// SAFETY: The caller is not asking questions for which this will tell lies.
`
``
483
`+
let b = unsafe { mem::transmute::<f16, u16>(self) };
`
``
484
`+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
`
``
485
`+
(0, Self::EXP_MASK) => FpCategory::Infinite,
`
``
486
`+
(0, 0) => FpCategory::Zero,
`
``
487
`+
(_, 0) => FpCategory::Subnormal,
`
``
488
`+
_ => FpCategory::Normal,
`
``
489
`+
}
`
``
490
`+
}
`
``
491
+
``
492
`+
/// This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
`
``
493
`+
/// FIXME(jubilee): In a just world, this would be the entire impl for classify,
`
``
494
`+
/// plus a transmute. We do not live in a just world, but we can make it more so.
`
``
495
`+
#[inline]
`
``
496
`+
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
`
``
497
`+
const fn classify_bits(b: u16) -> FpCategory {
`
``
498
`+
match (b & Self::MAN_MASK, b & Self::EXP_MASK) {
`
``
499
`+
(0, Self::EXP_MASK) => FpCategory::Infinite,
`
``
500
`+
(_, Self::EXP_MASK) => FpCategory::Nan,
`
``
501
`+
(0, 0) => FpCategory::Zero,
`
``
502
`+
(_, 0) => FpCategory::Subnormal,
`
``
503
`+
_ => FpCategory::Normal,
`
``
504
`+
}
`
``
505
`+
}
`
``
506
+
347
507
`` /// Returns true
if self
has a positive sign, including +0.0
, NaNs with
``
348
508
`/// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any
`
349
509
`/// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
`