Skip to main content

machina_softfloat/ops/
minmax.rs

1// SPDX-License-Identifier: MIT
2// IEEE 754 floating-point minimum and maximum.
3
4use crate::env::{ExcFlags, FloatEnv};
5use crate::parts::{nan_propagate, round_pack, unpack, FloatClass};
6use crate::types::{
7    BFloat16, Float128, Float16, Float32, Float64, FloatFormat, FloatX80,
8};
9
10/// IEEE 754-2008 minimum: returns the smaller of a and b.
11/// If either operand is SNaN, signals INVALID.
12/// If both are NaN, returns a quiet NaN.
13/// min(+0, -0) = -0.
14pub fn min<F: FloatFormat>(a: F, b: F, env: &mut FloatEnv) -> F {
15    let pa = unpack::<F>(a);
16    let pb = unpack::<F>(b);
17
18    // Signal INVALID for SNaN.
19    if pa.cls == FloatClass::SNaN || pb.cls == FloatClass::SNaN {
20        env.raise(ExcFlags::INVALID);
21    }
22
23    if pa.is_nan() && pb.is_nan() {
24        let mut r = nan_propagate(&pa, &pb, env);
25        return round_pack::<F>(&mut r, env);
26    }
27
28    if pa.is_nan() {
29        return b;
30    }
31    if pb.is_nan() {
32        return a;
33    }
34
35    // Both zeros: prefer -0.
36    if pa.cls == FloatClass::Zero && pb.cls == FloatClass::Zero {
37        return if pb.sign { b } else { a };
38    }
39
40    // Use comparison to pick smaller.
41    let a_lt_b = if pa.sign != pb.sign {
42        pa.sign // negative is smaller
43    } else if pa.sign {
44        // Both negative: larger magnitude is smaller.
45        if pa.exp != pb.exp {
46            pa.exp > pb.exp
47        } else {
48            pa.frac > pb.frac
49        }
50    } else {
51        // Both positive: smaller magnitude is smaller.
52        if pa.exp != pb.exp {
53            pa.exp < pb.exp
54        } else {
55            pa.frac < pb.frac
56        }
57    };
58
59    if a_lt_b {
60        a
61    } else {
62        b
63    }
64}
65
66/// IEEE 754-2008 maximum: returns the larger of a and b.
67/// If either operand is SNaN, signals INVALID.
68/// If both are NaN, returns a quiet NaN.
69/// max(+0, -0) = +0.
70pub fn max<F: FloatFormat>(a: F, b: F, env: &mut FloatEnv) -> F {
71    let pa = unpack::<F>(a);
72    let pb = unpack::<F>(b);
73
74    if pa.cls == FloatClass::SNaN || pb.cls == FloatClass::SNaN {
75        env.raise(ExcFlags::INVALID);
76    }
77
78    if pa.is_nan() && pb.is_nan() {
79        let mut r = nan_propagate(&pa, &pb, env);
80        return round_pack::<F>(&mut r, env);
81    }
82
83    if pa.is_nan() {
84        return b;
85    }
86    if pb.is_nan() {
87        return a;
88    }
89
90    // Both zeros: prefer +0.
91    if pa.cls == FloatClass::Zero && pb.cls == FloatClass::Zero {
92        return if pa.sign { b } else { a };
93    }
94
95    let a_gt_b = if pa.sign != pb.sign {
96        pb.sign // positive is larger
97    } else if pa.sign {
98        // Both negative: smaller magnitude is larger.
99        if pa.exp != pb.exp {
100            pa.exp < pb.exp
101        } else {
102            pa.frac < pb.frac
103        }
104    } else {
105        // Both positive: larger magnitude is larger.
106        if pa.exp != pb.exp {
107            pa.exp > pb.exp
108        } else {
109            pa.frac > pb.frac
110        }
111    };
112
113    if a_gt_b {
114        a
115    } else {
116        b
117    }
118}
119
120// ---------------------------------------------------------------
121// Convenience methods
122// ---------------------------------------------------------------
123
124macro_rules! impl_minmax {
125    ($ty:ty) => {
126        impl $ty {
127            pub fn min(self, other: Self, env: &mut FloatEnv) -> Self {
128                min::<Self>(self, other, env)
129            }
130            pub fn max(self, other: Self, env: &mut FloatEnv) -> Self {
131                max::<Self>(self, other, env)
132            }
133        }
134    };
135}
136
137impl_minmax!(Float16);
138impl_minmax!(BFloat16);
139impl_minmax!(Float32);
140impl_minmax!(Float64);
141impl_minmax!(Float128);
142impl_minmax!(FloatX80);