Skip to content

Commit 5fa14cd

Browse files
committed
min,max: Add tests for signaling NaNs and update documentation
We do handle signaling NaNs properly, with the exception of raising exceptions as IEEE 754 requires. Add tests to this effect for `fmin`, `fminimum`, `fminimum_num`, and the max variants.
1 parent 5e739d7 commit 5fa14cd

9 files changed

Lines changed: 314 additions & 63 deletions

File tree

libm/src/math/fmin_fmax.rs

Lines changed: 96 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,12 @@ pub fn fmaxf128(x: f128, y: f128) -> f128 {
7777
#[cfg(test)]
7878
mod tests {
7979
use super::*;
80+
use crate::support::hex_float::Hexi;
8081
use crate::support::{Float, Hexf};
8182

8283
fn fmin_spec_test<F: Float>(f: impl Fn(F, F) -> F) {
84+
// Note that (YaN, sNaN) and (sNaN, YaN) results differ from 754-2008. This is intentional,
85+
// see comments in the generic implementations.
8386
let cases = [
8487
(F::ZERO, F::ZERO, F::ZERO),
8588
(F::ZERO, F::ONE, F::ZERO),
@@ -88,13 +91,17 @@ mod tests {
8891
(F::ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
8992
(F::ZERO, F::NAN, F::ZERO),
9093
(F::ZERO, F::NEG_NAN, F::ZERO),
94+
(F::ZERO, F::SNAN, F::ZERO),
95+
(F::ZERO, F::NEG_SNAN, F::ZERO),
9196
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
9297
(F::NEG_ZERO, F::ONE, F::NEG_ZERO),
9398
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ONE),
9499
(F::NEG_ZERO, F::INFINITY, F::NEG_ZERO),
95100
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_INFINITY),
96101
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
97102
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
103+
(F::NEG_ZERO, F::SNAN, F::NEG_ZERO),
104+
(F::NEG_ZERO, F::NEG_SNAN, F::NEG_ZERO),
98105
(F::ONE, F::ZERO, F::ZERO),
99106
(F::ONE, F::NEG_ZERO, F::NEG_ZERO),
100107
(F::ONE, F::ONE, F::ONE),
@@ -103,6 +110,8 @@ mod tests {
103110
(F::ONE, F::NEG_INFINITY, F::NEG_INFINITY),
104111
(F::ONE, F::NAN, F::ONE),
105112
(F::ONE, F::NEG_NAN, F::ONE),
113+
(F::ONE, F::SNAN, F::ONE),
114+
(F::ONE, F::NEG_SNAN, F::ONE),
106115
(F::NEG_ONE, F::ZERO, F::NEG_ONE),
107116
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ONE),
108117
(F::NEG_ONE, F::ONE, F::NEG_ONE),
@@ -111,6 +120,8 @@ mod tests {
111120
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_INFINITY),
112121
(F::NEG_ONE, F::NAN, F::NEG_ONE),
113122
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
123+
(F::NEG_ONE, F::SNAN, F::NEG_ONE),
124+
(F::NEG_ONE, F::NEG_SNAN, F::NEG_ONE),
114125
(F::INFINITY, F::ZERO, F::ZERO),
115126
(F::INFINITY, F::NEG_ZERO, F::NEG_ZERO),
116127
(F::INFINITY, F::ONE, F::ONE),
@@ -119,6 +130,8 @@ mod tests {
119130
(F::INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
120131
(F::INFINITY, F::NAN, F::INFINITY),
121132
(F::INFINITY, F::NEG_NAN, F::INFINITY),
133+
(F::INFINITY, F::SNAN, F::INFINITY),
134+
(F::INFINITY, F::NEG_SNAN, F::INFINITY),
122135
(F::NEG_INFINITY, F::ZERO, F::NEG_INFINITY),
123136
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_INFINITY),
124137
(F::NEG_INFINITY, F::ONE, F::NEG_INFINITY),
@@ -127,6 +140,8 @@ mod tests {
127140
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
128141
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
129142
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
143+
(F::NEG_INFINITY, F::SNAN, F::NEG_INFINITY),
144+
(F::NEG_INFINITY, F::NEG_SNAN, F::NEG_INFINITY),
130145
(F::NAN, F::ZERO, F::ZERO),
131146
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
132147
(F::NAN, F::ONE, F::ONE),
@@ -140,19 +155,45 @@ mod tests {
140155
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
141156
(F::NEG_NAN, F::INFINITY, F::INFINITY),
142157
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
158+
(F::SNAN, F::ZERO, F::ZERO),
159+
(F::SNAN, F::NEG_ZERO, F::NEG_ZERO),
160+
(F::SNAN, F::ONE, F::ONE),
161+
(F::SNAN, F::NEG_ONE, F::NEG_ONE),
162+
(F::SNAN, F::INFINITY, F::INFINITY),
163+
(F::SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
164+
(F::SNAN, F::NAN, F::QSNAN),
165+
(F::NEG_SNAN, F::ZERO, F::ZERO),
166+
(F::NEG_SNAN, F::NEG_ZERO, F::NEG_ZERO),
167+
(F::NEG_SNAN, F::ONE, F::ONE),
168+
(F::NEG_SNAN, F::NEG_ONE, F::NEG_ONE),
169+
(F::NEG_SNAN, F::INFINITY, F::INFINITY),
170+
(F::NEG_SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
143171
];
144172

145173
for (x, y, res) in cases {
146174
let val = f(x, y);
147175
assert_biteq!(val, res, "fmin({}, {})", Hexf(x), Hexf(y));
148176
}
149177

150-
// Ordering between zeros and NaNs does not matter
178+
// Ordering between zeros does not matter
151179
assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO);
152180
assert_eq!(f(F::NEG_ZERO, F::ZERO), F::ZERO);
153-
assert!(f(F::NAN, F::NEG_NAN).is_nan());
154-
assert!(f(F::NEG_NAN, F::NAN).is_nan());
155-
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
181+
182+
// Selection between NaNs does not matter, it just must be quiet
183+
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
184+
assert!(f(F::NAN, F::NEG_SNAN).is_qnan());
185+
assert!(f(F::NAN, F::SNAN).is_qnan());
186+
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
187+
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
188+
assert!(f(F::NEG_NAN, F::NEG_SNAN).is_qnan());
189+
assert!(f(F::NEG_NAN, F::SNAN).is_qnan());
190+
assert!(f(F::NEG_SNAN, F::NAN).is_qnan());
191+
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_qnan());
192+
assert!(f(F::NEG_SNAN, F::NEG_SNAN).is_qnan());
193+
assert!(f(F::NEG_SNAN, F::SNAN).is_qnan());
194+
assert!(f(F::SNAN, F::NAN).is_qnan());
195+
assert!(f(F::SNAN, F::NEG_NAN).is_qnan());
196+
assert!(f(F::SNAN, F::NEG_SNAN).is_qnan());
156197
}
157198

158199
#[test]
@@ -186,13 +227,17 @@ mod tests {
186227
(F::ZERO, F::NEG_INFINITY, F::ZERO),
187228
(F::ZERO, F::NAN, F::ZERO),
188229
(F::ZERO, F::NEG_NAN, F::ZERO),
230+
(F::ZERO, F::SNAN, F::ZERO),
231+
(F::ZERO, F::NEG_SNAN, F::ZERO),
189232
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
190233
(F::NEG_ZERO, F::ONE, F::ONE),
191234
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO),
192235
(F::NEG_ZERO, F::INFINITY, F::INFINITY),
193236
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
194237
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
195238
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
239+
(F::NEG_ZERO, F::SNAN, F::NEG_ZERO),
240+
(F::NEG_ZERO, F::NEG_SNAN, F::NEG_ZERO),
196241
(F::ONE, F::ZERO, F::ONE),
197242
(F::ONE, F::NEG_ZERO, F::ONE),
198243
(F::ONE, F::ONE, F::ONE),
@@ -201,6 +246,8 @@ mod tests {
201246
(F::ONE, F::NEG_INFINITY, F::ONE),
202247
(F::ONE, F::NAN, F::ONE),
203248
(F::ONE, F::NEG_NAN, F::ONE),
249+
(F::ONE, F::SNAN, F::ONE),
250+
(F::ONE, F::NEG_SNAN, F::ONE),
204251
(F::NEG_ONE, F::ZERO, F::ZERO),
205252
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
206253
(F::NEG_ONE, F::ONE, F::ONE),
@@ -209,6 +256,8 @@ mod tests {
209256
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
210257
(F::NEG_ONE, F::NAN, F::NEG_ONE),
211258
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
259+
(F::NEG_ONE, F::SNAN, F::NEG_ONE),
260+
(F::NEG_ONE, F::NEG_SNAN, F::NEG_ONE),
212261
(F::INFINITY, F::ZERO, F::INFINITY),
213262
(F::INFINITY, F::NEG_ZERO, F::INFINITY),
214263
(F::INFINITY, F::ONE, F::INFINITY),
@@ -217,6 +266,8 @@ mod tests {
217266
(F::INFINITY, F::NEG_INFINITY, F::INFINITY),
218267
(F::INFINITY, F::NAN, F::INFINITY),
219268
(F::INFINITY, F::NEG_NAN, F::INFINITY),
269+
(F::INFINITY, F::SNAN, F::INFINITY),
270+
(F::INFINITY, F::NEG_SNAN, F::INFINITY),
220271
(F::NEG_INFINITY, F::ZERO, F::ZERO),
221272
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
222273
(F::NEG_INFINITY, F::ONE, F::ONE),
@@ -225,6 +276,8 @@ mod tests {
225276
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
226277
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
227278
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
279+
(F::NEG_INFINITY, F::SNAN, F::NEG_INFINITY),
280+
(F::NEG_INFINITY, F::NEG_SNAN, F::NEG_INFINITY),
228281
(F::NAN, F::ZERO, F::ZERO),
229282
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
230283
(F::NAN, F::ONE, F::ONE),
@@ -238,19 +291,53 @@ mod tests {
238291
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
239292
(F::NEG_NAN, F::INFINITY, F::INFINITY),
240293
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
294+
(F::SNAN, F::ZERO, F::ZERO),
295+
(F::SNAN, F::NEG_ZERO, F::NEG_ZERO),
296+
(F::SNAN, F::ONE, F::ONE),
297+
(F::SNAN, F::NEG_ONE, F::NEG_ONE),
298+
(F::SNAN, F::INFINITY, F::INFINITY),
299+
(F::SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
300+
(F::SNAN, F::SNAN, F::QSNAN),
301+
(F::NEG_SNAN, F::ZERO, F::ZERO),
302+
(F::NEG_SNAN, F::NEG_ZERO, F::NEG_ZERO),
303+
(F::NEG_SNAN, F::ONE, F::ONE),
304+
(F::NEG_SNAN, F::NEG_ONE, F::NEG_ONE),
305+
(F::NEG_SNAN, F::INFINITY, F::INFINITY),
306+
(F::NEG_SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
241307
];
242308

243309
for (x, y, res) in cases {
244310
let val = f(x, y);
245-
assert_biteq!(val, res, "fmax({}, {})", Hexf(x), Hexf(y));
311+
assert_biteq!(
312+
val,
313+
res,
314+
"fmax({}, {}) ({}, {})",
315+
Hexf(x),
316+
Hexf(y),
317+
Hexi(x.to_bits()),
318+
Hexi(y.to_bits()),
319+
);
246320
}
247321

248-
// Ordering between zeros and NaNs does not matter
322+
// Ordering between zeros
249323
assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO);
250324
assert_eq!(f(F::NEG_ZERO, F::ZERO), F::ZERO);
251-
assert!(f(F::NAN, F::NEG_NAN).is_nan());
252-
assert!(f(F::NEG_NAN, F::NAN).is_nan());
253-
assert!(f(F::NEG_NAN, F::NEG_NAN).is_nan());
325+
326+
// Selection between NaNs does not matter, it just must be quiet
327+
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
328+
assert!(f(F::NAN, F::NEG_SNAN).is_qnan());
329+
assert!(f(F::NAN, F::SNAN).is_qnan());
330+
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
331+
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
332+
assert!(f(F::NEG_NAN, F::NEG_SNAN).is_qnan());
333+
assert!(f(F::NEG_NAN, F::SNAN).is_qnan());
334+
assert!(f(F::NEG_SNAN, F::NAN).is_qnan());
335+
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_qnan());
336+
assert!(f(F::NEG_SNAN, F::NEG_SNAN).is_qnan());
337+
assert!(f(F::NEG_SNAN, F::SNAN).is_qnan());
338+
assert!(f(F::SNAN, F::NAN).is_qnan());
339+
assert!(f(F::SNAN, F::NEG_NAN).is_qnan());
340+
assert!(f(F::SNAN, F::NEG_SNAN).is_qnan());
254341
}
255342

256343
#[test]

0 commit comments

Comments
 (0)