Skip to main content

machina_softfloat/ops/
compare.rs

1// SPDX-License-Identifier: MIT
2// IEEE 754 floating-point comparison operations.
3
4use core::cmp::Ordering;
5
6use crate::env::{ExcFlags, FloatEnv};
7use crate::parts::{unpack, FloatClass};
8use crate::types::{
9    BFloat16, Float128, Float16, Float32, Float64, FloatFormat, FloatX80,
10};
11
12/// Signaling compare: returns None for unordered (NaN).
13/// Signals INVALID for any NaN operand.
14pub fn compare<F: FloatFormat>(
15    a: F,
16    b: F,
17    env: &mut FloatEnv,
18) -> Option<Ordering> {
19    let pa = unpack::<F>(a);
20    let pb = unpack::<F>(b);
21
22    if pa.is_nan() || pb.is_nan() {
23        env.raise(ExcFlags::INVALID);
24        return None;
25    }
26
27    compare_ordered(&pa, &pb)
28}
29
30/// Quiet compare: returns None for unordered (NaN).
31/// Only signals INVALID for SNaN, not QNaN.
32pub fn compare_quiet<F: FloatFormat>(
33    a: F,
34    b: F,
35    env: &mut FloatEnv,
36) -> Option<Ordering> {
37    let pa = unpack::<F>(a);
38    let pb = unpack::<F>(b);
39
40    if pa.is_nan() || pb.is_nan() {
41        if pa.cls == FloatClass::SNaN || pb.cls == FloatClass::SNaN {
42            env.raise(ExcFlags::INVALID);
43        }
44        return None;
45    }
46
47    compare_ordered(&pa, &pb)
48}
49
50fn compare_ordered(
51    a: &crate::parts::FloatParts,
52    b: &crate::parts::FloatParts,
53) -> Option<Ordering> {
54    // Both zeros are equal regardless of sign.
55    if a.cls == FloatClass::Zero && b.cls == FloatClass::Zero {
56        return Some(Ordering::Equal);
57    }
58
59    // Different signs.
60    if a.sign != b.sign {
61        // Negative < positive.
62        return if a.sign {
63            Some(Ordering::Less)
64        } else {
65            Some(Ordering::Greater)
66        };
67    }
68
69    // Same sign. Compare by (cls, exp, frac).
70    let raw_ord = if a.cls == FloatClass::Inf && b.cls == FloatClass::Inf {
71        Ordering::Equal
72    } else if a.cls == FloatClass::Inf {
73        Ordering::Greater
74    } else if b.cls == FloatClass::Inf {
75        Ordering::Less
76    } else if a.cls == FloatClass::Zero {
77        // b is non-zero.
78        Ordering::Less
79    } else if b.cls == FloatClass::Zero {
80        Ordering::Greater
81    } else {
82        // Both normal. Compare exponent first, then frac.
83        match a.exp.cmp(&b.exp) {
84            Ordering::Equal => a.frac.cmp(&b.frac),
85            other => other,
86        }
87    };
88
89    // If negative, reverse the ordering.
90    if a.sign {
91        Some(raw_ord.reverse())
92    } else {
93        Some(raw_ord)
94    }
95}
96
97/// Quiet equality: NaN != NaN.
98pub fn eq<F: FloatFormat>(a: F, b: F, env: &mut FloatEnv) -> bool {
99    compare_quiet::<F>(a, b, env) == Some(Ordering::Equal)
100}
101
102/// Signaling less-than.
103pub fn lt<F: FloatFormat>(a: F, b: F, env: &mut FloatEnv) -> bool {
104    compare::<F>(a, b, env) == Some(Ordering::Less)
105}
106
107/// Signaling less-than-or-equal.
108pub fn le<F: FloatFormat>(a: F, b: F, env: &mut FloatEnv) -> bool {
109    matches!(
110        compare::<F>(a, b, env),
111        Some(Ordering::Less) | Some(Ordering::Equal)
112    )
113}
114
115// ---------------------------------------------------------------
116// Convenience methods
117// ---------------------------------------------------------------
118
119macro_rules! impl_compare {
120    ($ty:ty) => {
121        impl $ty {
122            pub fn compare(
123                self,
124                other: Self,
125                env: &mut FloatEnv,
126            ) -> Option<Ordering> {
127                compare::<Self>(self, other, env)
128            }
129            pub fn compare_quiet(
130                self,
131                other: Self,
132                env: &mut FloatEnv,
133            ) -> Option<Ordering> {
134                compare_quiet::<Self>(self, other, env)
135            }
136            pub fn eq(self, other: Self, env: &mut FloatEnv) -> bool {
137                eq::<Self>(self, other, env)
138            }
139            pub fn lt(self, other: Self, env: &mut FloatEnv) -> bool {
140                lt::<Self>(self, other, env)
141            }
142            pub fn le(self, other: Self, env: &mut FloatEnv) -> bool {
143                le::<Self>(self, other, env)
144            }
145        }
146    };
147}
148
149impl_compare!(Float16);
150impl_compare!(BFloat16);
151impl_compare!(Float32);
152impl_compare!(Float64);
153impl_compare!(Float128);
154impl_compare!(FloatX80);