Skip to content

Commit b9903ea

Browse files
youdie006akern40
authored andcommitted
Fix inverted is_all_nan / is_any_nan / is_all_infinite / is_any_infinite
The boolean_ops! macro in src/numeric/impl_float_maths.rs had an inverted $all predicate: Zip::from(self).all(|&elt| !elt.$func()) tests "all elements are NOT NaN", the opposite of the documented "true if all elements are NaN". $any was defined as !self.$all(), so the double negation made is_any_* appear correct while is_all_* returned true for an all-finite array. Correct both predicates together (fixing only $all would break the is_any_* methods that ride on the double negation): $all -> Zip::from(self).all(|&elt| elt.$func()) $any -> Zip::from(self).any(|&elt| elt.$func()) Empty-array semantics are preserved (all -> true, any -> false). Fixes #1612
1 parent c95d814 commit b9903ea

2 files changed

Lines changed: 49 additions & 2 deletions

File tree

‎src/numeric/impl_float_maths.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ macro_rules! boolean_ops {
2020
$(#[$meta2])*
2121
#[must_use = "method returns a new boolean value and does not mutate the original value"]
2222
pub fn $all(&self) -> bool {
23-
$crate::Zip::from(self).all(|&elt| !elt.$func())
23+
$crate::Zip::from(self).all(|&elt| elt.$func())
2424
}
2525
$(#[$meta3])*
2626
#[must_use = "method returns a new boolean value and does not mutate the original value"]
2727
pub fn $any(&self) -> bool {
28-
!self.$all()
28+
$crate::Zip::from(self).any(|&elt| elt.$func())
2929
}
3030
};
3131
}

‎tests/nan_all_repro.rs‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Regression test for https://github.com/rust-ndarray/ndarray/issues/1612
2+
//
3+
// `is_all_nan`, `is_any_nan`, `is_all_infinite`, and `is_any_infinite` were
4+
// inverted: the `$all` predicate tested `!elt.$func()` ("all NOT nan") and the
5+
// `$any` predicate rode on the double negation, so `is_all_nan` returned `true`
6+
// for an all-finite array. These tests pin the correct behavior for both the
7+
// NaN and infinite families.
8+
9+
use ndarray::array;
10+
11+
#[test]
12+
fn is_all_nan_and_is_any_nan()
13+
{
14+
// All finite: neither all nor any element is NaN.
15+
let all_finite = array![1.0_f64, 2.0, 3.0];
16+
assert!(!all_finite.is_all_nan());
17+
assert!(!all_finite.is_any_nan());
18+
19+
// All NaN: every element is NaN.
20+
let all_nan = array![f64::NAN, f64::NAN, f64::NAN];
21+
assert!(all_nan.is_all_nan());
22+
assert!(all_nan.is_any_nan());
23+
24+
// Mixed: not all elements are NaN, but at least one is.
25+
let mixed = array![1.0_f64, f64::NAN, 3.0];
26+
assert!(!mixed.is_all_nan());
27+
assert!(mixed.is_any_nan());
28+
}
29+
30+
#[test]
31+
fn is_all_infinite_and_is_any_infinite()
32+
{
33+
// All finite: neither all nor any element is infinite.
34+
let all_finite = array![1.0_f64, 2.0, 3.0];
35+
assert!(!all_finite.is_all_infinite());
36+
assert!(!all_finite.is_any_infinite());
37+
38+
// All infinite: every element is infinite.
39+
let all_inf = array![f64::INFINITY, f64::NEG_INFINITY, f64::INFINITY];
40+
assert!(all_inf.is_all_infinite());
41+
assert!(all_inf.is_any_infinite());
42+
43+
// Mixed: not all elements are infinite, but at least one is.
44+
let mixed = array![1.0_f64, f64::INFINITY, 3.0];
45+
assert!(!mixed.is_all_infinite());
46+
assert!(mixed.is_any_infinite());
47+
}

0 commit comments

Comments
 (0)