|
17 | 17 |
|
18 | 18 | #![stable(feature = "rust1", since = "1.0.0")]
|
19 | 19 |
|
| 20 | +use cmp; |
20 | 21 | use mem;
|
21 | 22 | use num::FpCategory;
|
22 | 23 |
|
@@ -474,4 +475,135 @@ impl f32 {
|
474 | 475 | // It turns out the safety issues with sNaN were overblown! Hooray!
|
475 | 476 | unsafe { mem::transmute(v) }
|
476 | 477 | }
|
| 478 | + |
| 479 | + /// A 3-way version of the IEEE `totalOrder` predicate. |
| 480 | + /// |
| 481 | + /// This is most commonly used with `cmp::Total`, not directly. |
| 482 | + /// |
| 483 | + /// This is useful in situations where you run into the fact that `f32` is |
| 484 | + /// `PartialOrd`, but not `Ord`, like sorting. If you need a total order |
| 485 | + /// on arbitrary floating-point numbers you should use this -- or one of |
| 486 | + /// the related `total_*` methods -- they're implemented efficiently using |
| 487 | + /// just a few bitwise operations. |
| 488 | + /// |
| 489 | + /// This function mostly agrees with `PartialOrd` and the usual comparison |
| 490 | + /// operators in how it orders floating point numbers, but it additionally |
| 491 | + /// distinguishes negative from positive zero (it considers -0 as less than |
| 492 | + /// +0, whereas `==` considers them equal) and it orders Not-a-Number values |
| 493 | + /// relative to the numbers and distinguishes NaNs with different bit patterns. |
| 494 | + /// |
| 495 | + /// NaNs with positive sign are ordered greater than all other floating-point |
| 496 | + /// values including positive infinity, while NaNs with negative sign are |
| 497 | + /// ordered the least, below negative infinity. Two different NaNs with the |
| 498 | + /// same sign are ordered first by whether the are signaling (signaling is |
| 499 | + /// less than quiet if the sign is positive, reverse if negative) and second |
| 500 | + /// by their payload interpreted as integer (reversed order if the sign is negative). |
| 501 | + /// |
| 502 | + /// This means all different (canonical) floating point bit patterns are |
| 503 | + /// placed in a linear order, given below in ascending order: |
| 504 | + /// |
| 505 | + /// - Quiet Not-a-Number with negative sign (ordered by payload, descending) |
| 506 | + /// - Signaling Not-a-Number with negative sign (ordered by payload, descending) |
| 507 | + /// - Negative infinity |
| 508 | + /// - Negative finite non-zero numbers (ordered in the usual way) |
| 509 | + /// - Negative zero |
| 510 | + /// - Positive zero |
| 511 | + /// - Positive finite non-zero numbers (ordered in the usual way) |
| 512 | + /// - Positive infinity |
| 513 | + /// - Signaling Not-a-Number with positive sign (ordered by payload, ascending) |
| 514 | + /// - Quiet Not-a-Number with positive sign (ordered by payload, ascending) |
| 515 | + /// |
| 516 | + /// # Examples |
| 517 | + /// |
| 518 | + /// Most follow the normal ordering: |
| 519 | + /// ``` |
| 520 | + /// #![feature(float_total_cmp)] |
| 521 | + /// use std::cmp::Ordering::*; |
| 522 | + /// |
| 523 | + /// assert_eq!(f32::total_cmp(1.0, 2.0), Less); |
| 524 | + /// assert_eq!(f32::total_cmp(1.0, 1.0), Equal); |
| 525 | + /// assert_eq!(f32::total_cmp(2.0, 1.0), Greater); |
| 526 | + /// assert_eq!(f32::total_cmp(-1.0, 1.0), Less); |
| 527 | + /// assert_eq!(f32::total_cmp(1.0, -1.0), Greater); |
| 528 | + /// assert_eq!(f32::total_cmp(-1.0, -2.0), Greater); |
| 529 | + /// assert_eq!(f32::total_cmp(-1.0, -1.0), Equal); |
| 530 | + /// assert_eq!(f32::total_cmp(-2.0, -1.0), Less); |
| 531 | + /// assert_eq!(f32::total_cmp(-std::f32::MAX, -1.0e9), Less); |
| 532 | + /// assert_eq!(f32::total_cmp(std::f32::MAX, 1.0e9), Greater); |
| 533 | + /// ``` |
| 534 | + /// |
| 535 | + /// Zeros and NaNs: |
| 536 | + /// ``` |
| 537 | + /// #![feature(float_total_cmp)] |
| 538 | + /// use std::cmp::Ordering::*; |
| 539 | + /// |
| 540 | + /// assert_eq!(f32::partial_cmp(&0.0, &-0.0), Some(Equal)); |
| 541 | + /// assert_eq!(f32::total_cmp(-0.0, 0.0), Less); |
| 542 | + /// assert_eq!(f32::total_cmp(0.0, -0.0), Greater); |
| 543 | + /// |
| 544 | + /// assert_eq!(f32::partial_cmp(&std::f32::NAN, &std::f32::NAN), None); |
| 545 | + /// assert_eq!(f32::total_cmp(-std::f32::NAN, std::f32::NAN), Less); |
| 546 | + /// assert_eq!(f32::total_cmp(std::f32::NAN, std::f32::NAN), Equal); |
| 547 | + /// assert_eq!(f32::total_cmp(std::f32::NAN, -std::f32::NAN), Greater); |
| 548 | + /// assert_eq!(f32::total_cmp(std::f32::NAN, std::f32::INFINITY), Greater); |
| 549 | + /// assert_eq!(f32::total_cmp(-std::f32::NAN, -std::f32::INFINITY), Less); |
| 550 | + /// ``` |
| 551 | + #[unstable(feature = "float_total_cmp", issue = "55339")] |
| 552 | + #[inline] |
| 553 | + pub fn total_cmp(self, other: f32) -> cmp::Ordering { |
| 554 | + self.total_cmp_key().cmp(&other.total_cmp_key()) |
| 555 | + } |
| 556 | + |
| 557 | + #[inline] |
| 558 | + fn total_cmp_key(self) -> i32 { |
| 559 | + let x = self.to_bits() as i32; |
| 560 | + // Flip the bottom 31 bits if the high bit is set |
| 561 | + x ^ ((x >> 31) as u32 >> 1) as i32 |
| 562 | + } |
| 563 | +} |
| 564 | + |
| 565 | +#[unstable(feature = "float_total_cmp", issue = "55339")] |
| 566 | +impl PartialEq for cmp::Total<f32> { |
| 567 | + fn eq(&self, other: &Self) -> bool { |
| 568 | + self.0.to_bits() == other.0.to_bits() |
| 569 | + } |
| 570 | +} |
| 571 | + |
| 572 | +#[unstable(feature = "float_total_cmp", issue = "55339")] |
| 573 | +impl Eq for cmp::Total<f32> {} |
| 574 | + |
| 575 | +#[unstable(feature = "float_total_cmp", issue = "55339")] |
| 576 | +impl PartialOrd for cmp::Total<f32> { |
| 577 | + fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> { |
| 578 | + Some(self.cmp(other)) |
| 579 | + } |
| 580 | +} |
| 581 | + |
| 582 | +/// See `f32::total_cmp` for details. |
| 583 | +/// |
| 584 | +/// Using this to sort floats: |
| 585 | +/// ``` |
| 586 | +/// #![feature(float_total_cmp)] |
| 587 | +/// |
| 588 | +/// let mut a = [ |
| 589 | +/// 1.0, |
| 590 | +/// -1.0, |
| 591 | +/// 0.0, |
| 592 | +/// -0.0, |
| 593 | +/// std::f32::NAN, |
| 594 | +/// -std::f32::NAN, |
| 595 | +/// std::f32::INFINITY, |
| 596 | +/// std::f32::NEG_INFINITY, |
| 597 | +/// ]; |
| 598 | +/// a.sort_by_key(|x| std::cmp::Total(*x)); |
| 599 | +/// assert_eq!( |
| 600 | +/// format!("{:?}", a), |
| 601 | +/// "[NaN, -inf, -1.0, -0.0, 0.0, 1.0, inf, NaN]" |
| 602 | +/// ); |
| 603 | +/// ``` |
| 604 | +#[unstable(feature = "float_total_cmp", issue = "55339")] |
| 605 | +impl Ord for cmp::Total<f32> { |
| 606 | + fn cmp(&self, other: &Self) -> cmp::Ordering { |
| 607 | + self.0.total_cmp(other.0) |
| 608 | + } |
477 | 609 | }
|
0 commit comments