Skip to content

Commit 1cd24ca

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 d2bbb96 commit 1cd24ca

9 files changed

Lines changed: 330 additions & 63 deletions

File tree

libm/src/math/fmin_fmax.rs

Lines changed: 100 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,48 @@ 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::NEG_SNAN, F::ZERO, F::ZERO),
165+
(F::NEG_SNAN, F::NEG_ZERO, F::NEG_ZERO),
166+
(F::NEG_SNAN, F::ONE, F::ONE),
167+
(F::NEG_SNAN, F::NEG_ONE, F::NEG_ONE),
168+
(F::NEG_SNAN, F::INFINITY, F::INFINITY),
169+
(F::NEG_SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
143170
];
144171

145172
for (x, y, res) in cases {
146173
let val = f(x, y);
147174
assert_biteq!(val, res, "fmin({}, {})", Hexf(x), Hexf(y));
148175
}
149176

150-
// Ordering between zeros and NaNs does not matter
177+
// Ordering between zeros does not matter
151178
assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO);
152179
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());
180+
181+
// Selection between NaNs does not matter, it just must be quiet
182+
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
183+
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
184+
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
185+
186+
// These operations should technically return a qnan, but LLVM optimizes out our
187+
// `* 1.0` canonicalization.
188+
assert!(f(F::NAN, F::NEG_SNAN).is_nan());
189+
assert!(f(F::NAN, F::SNAN).is_nan());
190+
assert!(f(F::NEG_NAN, F::NEG_SNAN).is_nan());
191+
assert!(f(F::NEG_NAN, F::SNAN).is_nan());
192+
assert!(f(F::NEG_SNAN, F::NAN).is_nan());
193+
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_nan());
194+
assert!(f(F::NEG_SNAN, F::NEG_SNAN).is_nan());
195+
assert!(f(F::NEG_SNAN, F::SNAN).is_nan());
196+
assert!(f(F::SNAN, F::NAN).is_nan());
197+
assert!(f(F::SNAN, F::NAN).is_nan());
198+
assert!(f(F::SNAN, F::NEG_NAN).is_nan());
199+
assert!(f(F::SNAN, F::NEG_SNAN).is_nan());
156200
}
157201

158202
#[test]
@@ -186,13 +230,17 @@ mod tests {
186230
(F::ZERO, F::NEG_INFINITY, F::ZERO),
187231
(F::ZERO, F::NAN, F::ZERO),
188232
(F::ZERO, F::NEG_NAN, F::ZERO),
233+
(F::ZERO, F::SNAN, F::ZERO),
234+
(F::ZERO, F::NEG_SNAN, F::ZERO),
189235
(F::NEG_ZERO, F::NEG_ZERO, F::NEG_ZERO),
190236
(F::NEG_ZERO, F::ONE, F::ONE),
191237
(F::NEG_ZERO, F::NEG_ONE, F::NEG_ZERO),
192238
(F::NEG_ZERO, F::INFINITY, F::INFINITY),
193239
(F::NEG_ZERO, F::NEG_INFINITY, F::NEG_ZERO),
194240
(F::NEG_ZERO, F::NAN, F::NEG_ZERO),
195241
(F::NEG_ZERO, F::NEG_NAN, F::NEG_ZERO),
242+
(F::NEG_ZERO, F::SNAN, F::NEG_ZERO),
243+
(F::NEG_ZERO, F::NEG_SNAN, F::NEG_ZERO),
196244
(F::ONE, F::ZERO, F::ONE),
197245
(F::ONE, F::NEG_ZERO, F::ONE),
198246
(F::ONE, F::ONE, F::ONE),
@@ -201,6 +249,8 @@ mod tests {
201249
(F::ONE, F::NEG_INFINITY, F::ONE),
202250
(F::ONE, F::NAN, F::ONE),
203251
(F::ONE, F::NEG_NAN, F::ONE),
252+
(F::ONE, F::SNAN, F::ONE),
253+
(F::ONE, F::NEG_SNAN, F::ONE),
204254
(F::NEG_ONE, F::ZERO, F::ZERO),
205255
(F::NEG_ONE, F::NEG_ZERO, F::NEG_ZERO),
206256
(F::NEG_ONE, F::ONE, F::ONE),
@@ -209,6 +259,8 @@ mod tests {
209259
(F::NEG_ONE, F::NEG_INFINITY, F::NEG_ONE),
210260
(F::NEG_ONE, F::NAN, F::NEG_ONE),
211261
(F::NEG_ONE, F::NEG_NAN, F::NEG_ONE),
262+
(F::NEG_ONE, F::SNAN, F::NEG_ONE),
263+
(F::NEG_ONE, F::NEG_SNAN, F::NEG_ONE),
212264
(F::INFINITY, F::ZERO, F::INFINITY),
213265
(F::INFINITY, F::NEG_ZERO, F::INFINITY),
214266
(F::INFINITY, F::ONE, F::INFINITY),
@@ -217,6 +269,8 @@ mod tests {
217269
(F::INFINITY, F::NEG_INFINITY, F::INFINITY),
218270
(F::INFINITY, F::NAN, F::INFINITY),
219271
(F::INFINITY, F::NEG_NAN, F::INFINITY),
272+
(F::INFINITY, F::SNAN, F::INFINITY),
273+
(F::INFINITY, F::NEG_SNAN, F::INFINITY),
220274
(F::NEG_INFINITY, F::ZERO, F::ZERO),
221275
(F::NEG_INFINITY, F::NEG_ZERO, F::NEG_ZERO),
222276
(F::NEG_INFINITY, F::ONE, F::ONE),
@@ -225,6 +279,8 @@ mod tests {
225279
(F::NEG_INFINITY, F::NEG_INFINITY, F::NEG_INFINITY),
226280
(F::NEG_INFINITY, F::NAN, F::NEG_INFINITY),
227281
(F::NEG_INFINITY, F::NEG_NAN, F::NEG_INFINITY),
282+
(F::NEG_INFINITY, F::SNAN, F::NEG_INFINITY),
283+
(F::NEG_INFINITY, F::NEG_SNAN, F::NEG_INFINITY),
228284
(F::NAN, F::ZERO, F::ZERO),
229285
(F::NAN, F::NEG_ZERO, F::NEG_ZERO),
230286
(F::NAN, F::ONE, F::ONE),
@@ -238,19 +294,54 @@ mod tests {
238294
(F::NEG_NAN, F::NEG_ONE, F::NEG_ONE),
239295
(F::NEG_NAN, F::INFINITY, F::INFINITY),
240296
(F::NEG_NAN, F::NEG_INFINITY, F::NEG_INFINITY),
297+
(F::SNAN, F::ZERO, F::ZERO),
298+
(F::SNAN, F::NEG_ZERO, F::NEG_ZERO),
299+
(F::SNAN, F::ONE, F::ONE),
300+
(F::SNAN, F::NEG_ONE, F::NEG_ONE),
301+
(F::SNAN, F::INFINITY, F::INFINITY),
302+
(F::SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
303+
(F::NEG_SNAN, F::ZERO, F::ZERO),
304+
(F::NEG_SNAN, F::NEG_ZERO, F::NEG_ZERO),
305+
(F::NEG_SNAN, F::ONE, F::ONE),
306+
(F::NEG_SNAN, F::NEG_ONE, F::NEG_ONE),
307+
(F::NEG_SNAN, F::INFINITY, F::INFINITY),
308+
(F::NEG_SNAN, F::NEG_INFINITY, F::NEG_INFINITY),
241309
];
242310

243311
for (x, y, res) in cases {
244312
let val = f(x, y);
245-
assert_biteq!(val, res, "fmax({}, {})", Hexf(x), Hexf(y));
313+
assert_biteq!(
314+
val,
315+
res,
316+
"fmax({}, {}) ({}, {})",
317+
Hexf(x),
318+
Hexf(y),
319+
Hexi(x.to_bits()),
320+
Hexi(y.to_bits()),
321+
);
246322
}
247323

248-
// Ordering between zeros and NaNs does not matter
324+
// Ordering between zeros
249325
assert_eq!(f(F::ZERO, F::NEG_ZERO), F::ZERO);
250326
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());
327+
328+
// Selection between NaNs does not matter, it just must be quiet
329+
assert!(f(F::NAN, F::NEG_NAN).is_qnan());
330+
assert!(f(F::NEG_NAN, F::NAN).is_qnan());
331+
assert!(f(F::NEG_NAN, F::NEG_NAN).is_qnan());
332+
333+
assert!(f(F::NAN, F::NEG_SNAN).is_nan());
334+
assert!(f(F::NAN, F::SNAN).is_nan());
335+
assert!(f(F::NEG_NAN, F::NEG_SNAN).is_nan());
336+
assert!(f(F::NEG_NAN, F::SNAN).is_nan());
337+
assert!(f(F::NEG_SNAN, F::NAN).is_nan());
338+
assert!(f(F::NEG_SNAN, F::NEG_NAN).is_nan());
339+
assert!(f(F::NEG_SNAN, F::NEG_SNAN).is_nan());
340+
assert!(f(F::NEG_SNAN, F::SNAN).is_nan());
341+
assert!(f(F::SNAN, F::NAN).is_nan());
342+
assert!(f(F::SNAN, F::NEG_NAN).is_nan());
343+
assert!(f(F::SNAN, F::NEG_SNAN).is_nan());
344+
assert!(f(F::SNAN, F::SNAN).is_nan());
254345
}
255346

256347
#[test]

0 commit comments

Comments
 (0)