|
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
|
|
0 commit comments