| 
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