|  | 
| 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