|
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 = "f128", issue = "116909")]
|
@@ -251,6 +252,12 @@ impl f128 {
|
251 | 252 | #[cfg(not(bootstrap))]
|
252 | 253 | pub(crate) const SIGN_MASK: u128 = 0x8000_0000_0000_0000_0000_0000_0000_0000;
|
253 | 254 |
|
| 255 | + /// Exponent mask |
| 256 | + pub(crate) const EXP_MASK: u128 = 0x7fff_0000_0000_0000_0000_0000_0000_0000; |
| 257 | + |
| 258 | + /// Mantissa mask |
| 259 | + pub(crate) const MAN_MASK: u128 = 0x0000_ffff_ffff_ffff_ffff_ffff_ffff_ffff; |
| 260 | + |
254 | 261 | /// Minimum representable positive value (min subnormal)
|
255 | 262 | #[cfg(not(bootstrap))]
|
256 | 263 | const TINY_BITS: u128 = 0x1;
|
@@ -354,6 +361,169 @@ impl f128 {
|
354 | 361 | self.abs_private() < Self::INFINITY
|
355 | 362 | }
|
356 | 363 |
|
| 364 | + /// Returns `true` if the number is [subnormal]. |
| 365 | + /// |
| 366 | + /// ``` |
| 367 | + /// #![feature(f128)] |
| 368 | + /// # // FIXME(f16_f128): remove when `eqtf2` is available |
| 369 | + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { |
| 370 | + /// |
| 371 | + /// let min = f128::MIN_POSITIVE; // 3.362103143e-4932f128 |
| 372 | + /// let max = f128::MAX; |
| 373 | + /// let lower_than_min = 1.0e-4960_f128; |
| 374 | + /// let zero = 0.0_f128; |
| 375 | + /// |
| 376 | + /// assert!(!min.is_subnormal()); |
| 377 | + /// assert!(!max.is_subnormal()); |
| 378 | + /// |
| 379 | + /// assert!(!zero.is_subnormal()); |
| 380 | + /// assert!(!f128::NAN.is_subnormal()); |
| 381 | + /// assert!(!f128::INFINITY.is_subnormal()); |
| 382 | + /// // Values between `0` and `min` are Subnormal. |
| 383 | + /// assert!(lower_than_min.is_subnormal()); |
| 384 | + /// # } |
| 385 | + /// ``` |
| 386 | + /// |
| 387 | + /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number |
| 388 | + #[inline] |
| 389 | + #[must_use] |
| 390 | + #[cfg(not(bootstrap))] |
| 391 | + #[unstable(feature = "f128", issue = "116909")] |
| 392 | + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] |
| 393 | + pub const fn is_subnormal(self) -> bool { |
| 394 | + matches!(self.classify(), FpCategory::Subnormal) |
| 395 | + } |
| 396 | + |
| 397 | + /// Returns `true` if the number is neither zero, infinite, |
| 398 | + /// [subnormal], or NaN. |
| 399 | + /// |
| 400 | + /// ``` |
| 401 | + /// #![feature(f128)] |
| 402 | + /// # // FIXME(f16_f128): remove when `eqtf2` is available |
| 403 | + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { |
| 404 | + /// |
| 405 | + /// let min = f128::MIN_POSITIVE; // 3.362103143e-4932f128 |
| 406 | + /// let max = f128::MAX; |
| 407 | + /// let lower_than_min = 1.0e-4960_f128; |
| 408 | + /// let zero = 0.0_f128; |
| 409 | + /// |
| 410 | + /// assert!(min.is_normal()); |
| 411 | + /// assert!(max.is_normal()); |
| 412 | + /// |
| 413 | + /// assert!(!zero.is_normal()); |
| 414 | + /// assert!(!f128::NAN.is_normal()); |
| 415 | + /// assert!(!f128::INFINITY.is_normal()); |
| 416 | + /// // Values between `0` and `min` are Subnormal. |
| 417 | + /// assert!(!lower_than_min.is_normal()); |
| 418 | + /// # } |
| 419 | + /// ``` |
| 420 | + /// |
| 421 | + /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number |
| 422 | + #[inline] |
| 423 | + #[must_use] |
| 424 | + #[cfg(not(bootstrap))] |
| 425 | + #[unstable(feature = "f128", issue = "116909")] |
| 426 | + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] |
| 427 | + pub const fn is_normal(self) -> bool { |
| 428 | + matches!(self.classify(), FpCategory::Normal) |
| 429 | + } |
| 430 | + |
| 431 | + /// Returns the floating point category of the number. If only one property |
| 432 | + /// is going to be tested, it is generally faster to use the specific |
| 433 | + /// predicate instead. |
| 434 | + /// |
| 435 | + /// ``` |
| 436 | + /// #![feature(f128)] |
| 437 | + /// # // FIXME(f16_f128): remove when `eqtf2` is available |
| 438 | + /// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] { |
| 439 | + /// |
| 440 | + /// use std::num::FpCategory; |
| 441 | + /// |
| 442 | + /// let num = 12.4_f128; |
| 443 | + /// let inf = f128::INFINITY; |
| 444 | + /// |
| 445 | + /// assert_eq!(num.classify(), FpCategory::Normal); |
| 446 | + /// assert_eq!(inf.classify(), FpCategory::Infinite); |
| 447 | + /// # } |
| 448 | + /// ``` |
| 449 | + #[inline] |
| 450 | + #[cfg(not(bootstrap))] |
| 451 | + #[unstable(feature = "f128", issue = "116909")] |
| 452 | + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] |
| 453 | + pub const fn classify(self) -> FpCategory { |
| 454 | + // A previous implementation for f32/f64 tried to only use bitmask-based checks, |
| 455 | + // using `to_bits` to transmute the float to its bit repr and match on that. |
| 456 | + // Unfortunately, floating point numbers can be much worse than that. |
| 457 | + // This also needs to not result in recursive evaluations of `to_bits`. |
| 458 | + // |
| 459 | + // On some processors, in some cases, LLVM will "helpfully" lower floating point ops, |
| 460 | + // in spite of a request for them using standard IEEE types, to things like x87 operations. |
| 461 | + // These have an f64's mantissa, but can have a larger than normal exponent. |
| 462 | + // FIXME(jubilee): Using x87 operations is never necessary in order to function |
| 463 | + // on x86 processors for Rust-to-Rust calls, so this issue should not happen. |
| 464 | + // Code generation should be adjusted to use non-C calling conventions, avoiding this. |
| 465 | + // |
| 466 | + // This is likely not a problem for `f128` since it can't fit into x87 flaots, but we |
| 467 | + // keep things consistent anyway. |
| 468 | + if self.is_infinite() { |
| 469 | + // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask. |
| 470 | + FpCategory::Infinite |
| 471 | + } else if self.is_nan() { |
| 472 | + // And it may not be NaN, as it can simply be an "overextended" finite value. |
| 473 | + FpCategory::Nan |
| 474 | + } else { |
| 475 | + // However, std can't simply compare to zero to check for zero, either, |
| 476 | + // as correctness requires avoiding equality tests that may be Subnormal == -0.0 |
| 477 | + // because it may be wrong under "denormals are zero" and "flush to zero" modes. |
| 478 | + // Most of std's targets don't use those, but they are used for thumbv7neon. |
| 479 | + // So, this does use bitpattern matching for the rest. |
| 480 | + |
| 481 | + // SAFETY: f128 to u128 is fine. Usually. |
| 482 | + // If classify has gotten this far, the value is definitely in one of these categories. |
| 483 | + unsafe { f128::partial_classify(self) } |
| 484 | + } |
| 485 | + } |
| 486 | + |
| 487 | + /// This doesn't actually return a right answer for NaN on purpose, |
| 488 | + /// seeing as how it cannot correctly discern between a floating point NaN, |
| 489 | + /// and some normal floating point numbers truncated from an x87 FPU. |
| 490 | + /// FIXME(jubilee): This probably could at least answer things correctly for Infinity, |
| 491 | + /// like the f64 version does, but I need to run more checks on how things go on x86. |
| 492 | + /// I fear losing mantissa data that would have answered that differently. |
| 493 | + /// |
| 494 | + /// # Safety |
| 495 | + /// This requires making sure you call this function for values it answers correctly on, |
| 496 | + /// otherwise it returns a wrong answer. This is not important for memory safety per se, |
| 497 | + /// but getting floats correct is important for not accidentally leaking const eval |
| 498 | + /// runtime-deviating logic which may or may not be acceptable. |
| 499 | + #[inline] |
| 500 | + #[cfg(not(bootstrap))] |
| 501 | + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] |
| 502 | + const unsafe fn partial_classify(self) -> FpCategory { |
| 503 | + // SAFETY: The caller is not asking questions for which this will tell lies. |
| 504 | + let b = unsafe { mem::transmute::<f128, u128>(self) }; |
| 505 | + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { |
| 506 | + (0, 0) => FpCategory::Zero, |
| 507 | + (_, 0) => FpCategory::Subnormal, |
| 508 | + _ => FpCategory::Normal, |
| 509 | + } |
| 510 | + } |
| 511 | + |
| 512 | + /// This operates on bits, and only bits, so it can ignore concerns about weird FPUs. |
| 513 | + /// FIXME(jubilee): In a just world, this would be the entire impl for classify, |
| 514 | + /// plus a transmute. We do not live in a just world, but we can make it more so. |
| 515 | + #[inline] |
| 516 | + #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")] |
| 517 | + const fn classify_bits(b: u128) -> FpCategory { |
| 518 | + match (b & Self::MAN_MASK, b & Self::EXP_MASK) { |
| 519 | + (0, Self::EXP_MASK) => FpCategory::Infinite, |
| 520 | + (_, Self::EXP_MASK) => FpCategory::Nan, |
| 521 | + (0, 0) => FpCategory::Zero, |
| 522 | + (_, 0) => FpCategory::Subnormal, |
| 523 | + _ => FpCategory::Normal, |
| 524 | + } |
| 525 | + } |
| 526 | + |
357 | 527 | /// Returns `true` if `self` has a positive sign, including `+0.0`, NaNs with
|
358 | 528 | /// positive sign bit and positive infinity. Note that IEEE 754 doesn't assign any
|
359 | 529 | /// meaning to the sign bit in case of a NaN, and as Rust doesn't guarantee that
|
|
0 commit comments