Skip to main content

zerodds_float128/
cmp.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! Comparison: the IEEE one, and the total one a key needs.
4
5use core::cmp::Ordering;
6
7use crate::float::{Float128, SIGN_MASK};
8
9impl PartialEq for Float128 {
10    /// IEEE 754-2008 clause 5.11 `compareQuietEqual`.
11    ///
12    /// `-0.0 == 0.0`, and a NaN is equal to nothing, itself included.
13    /// The same contract `f64` has, so a generated structure holding a
14    /// `Float128` compares the way the same structure holding a `double`
15    /// does — a binding whose equality changed with the member's width
16    /// would be a binding that fails a round trip test for a reason that
17    /// has nothing to do with the wire.
18    fn eq(&self, other: &Self) -> bool {
19        if self.is_nan() || other.is_nan() {
20            return false;
21        }
22        if self.is_zero() && other.is_zero() {
23            return true;
24        }
25        self.to_bits() == other.to_bits()
26    }
27}
28
29impl PartialOrd for Float128 {
30    /// IEEE 754-2008 clause 5.11. `None` when either side is a NaN, the
31    /// unordered case.
32    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
33        if self.is_nan() || other.is_nan() {
34            return None;
35        }
36        if self.is_zero() && other.is_zero() {
37            return Some(Ordering::Equal);
38        }
39        Some(ordered_key(*self).cmp(&ordered_key(*other)))
40    }
41}
42
43impl Float128 {
44    /// IEEE 754-2008 clause 5.10 `totalOrder`.
45    ///
46    /// A total order over every bit pattern: `-NaN < -inf < … < -0.0 <
47    /// +0.0 < … < +inf < +NaN`, with NaNs ordered by payload. This is
48    /// what a `@key` member and a sorted expectation need — `partial_cmp`
49    /// returning `None` for a NaN key would leave the sort order to
50    /// whatever the caller's comparator does with `None`, which is how
51    /// two peers disagree about instance order for a sample neither
52    /// considers unusual.
53    ///
54    /// The same construction `f64::total_cmp` uses: read the pattern as
55    /// a sign-magnitude integer and map it onto a two's-complement one.
56    #[must_use]
57    pub fn total_cmp(self, other: Self) -> Ordering {
58        ordered_key(self).cmp(&ordered_key(other))
59    }
60
61    /// The larger of two values under [`Self::total_cmp`].
62    #[must_use]
63    pub fn total_max(self, other: Self) -> Self {
64        if self.total_cmp(other) == Ordering::Less {
65            other
66        } else {
67            self
68        }
69    }
70
71    /// The smaller of two values under [`Self::total_cmp`].
72    #[must_use]
73    pub fn total_min(self, other: Self) -> Self {
74        if self.total_cmp(other) == Ordering::Greater {
75            other
76        } else {
77            self
78        }
79    }
80}
81
82/// The bit pattern mapped so that unsigned integer order *is* numeric
83/// order.
84///
85/// A negative value's pattern counts the wrong way round — a larger
86/// magnitude is a larger unsigned pattern but a smaller number — so the
87/// non-sign bits are complemented for negatives and the sign bit is set
88/// for positives.
89fn ordered_key(value: Float128) -> u128 {
90    let bits = value.to_bits();
91    if bits & SIGN_MASK == 0 {
92        bits | SIGN_MASK
93    } else {
94        !bits
95    }
96}