Skip to content

Commit 516f922

Browse files
authored
ALPArray correctly handles comparison to NaN, inf and -inf (#2795)
1 parent 20e7c60 commit 516f922

File tree

1 file changed

+68
-16
lines changed

1 file changed

+68
-16
lines changed

encodings/alp/src/alp/compute/compare.rs

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::fmt::Debug;
33
use vortex_array::arrays::ConstantArray;
44
use vortex_array::compute::{CompareFn, Operator, compare};
55
use vortex_array::{Array, ArrayRef};
6+
use vortex_dtype::NativePType;
67
use vortex_error::{VortexResult, vortex_bail};
78
use vortex_scalar::{PrimitiveScalar, Scalar};
89

@@ -74,27 +75,50 @@ where
7475
// Since this value is not encodable it cannot be equal to any value in the encoded
7576
// array, hence != to all values in the encoded array.
7677
Operator::NotEq => Ok(Some(ConstantArray::new(true, alp.len()).into_array())),
77-
Operator::Gt | Operator::Gte => Ok(Some(compare(
78-
alp.encoded(),
79-
&ConstantArray::new(F::encode_above(value, exponents), alp.len()),
80-
// Since the encoded value is unencodable gte is equivalent to gt.
81-
// Consider a value v, between two encodable values v_l (just less) and
82-
// v_a (just above), then for all encodable values (u), v > u <=> v_g >= u
83-
Operator::Gte,
84-
)?)),
85-
Operator::Lt | Operator::Lte => Ok(Some(compare(
86-
alp.encoded(),
87-
&ConstantArray::new(F::encode_below(value, exponents), alp.len()),
88-
// Since the encoded values unencodable lt is equivalent to lte.
89-
// See Gt | Gte for further explanation.
90-
Operator::Lte,
91-
)?)),
78+
Operator::Gt | Operator::Gte => {
79+
// Per IEEE 754 totalOrder semantics the ordering is -Nan < -Inf < Inf < Nan.
80+
// All values in the encoded array are definitely finite
81+
let is_not_finite = value.is_infinite() || NativePType::is_nan(value);
82+
if is_not_finite {
83+
Ok(Some(
84+
ConstantArray::new(value.is_sign_negative(), alp.len()).into_array(),
85+
))
86+
} else {
87+
Ok(Some(compare(
88+
alp.encoded(),
89+
&ConstantArray::new(F::encode_above(value, exponents), alp.len()),
90+
// Since the encoded value is unencodable gte is equivalent to gt.
91+
// Consider a value v, between two encodable values v_l (just less) and
92+
// v_a (just above), then for all encodable values (u), v > u <=> v_g >= u
93+
Operator::Gte,
94+
)?))
95+
}
96+
}
97+
Operator::Lt | Operator::Lte => {
98+
// Per IEEE 754 totalOrder semantics the ordering is -Nan < -Inf < Inf < Nan.
99+
// All values in the encoded array are definitely finite
100+
let is_not_finite = value.is_infinite() || NativePType::is_nan(value);
101+
if is_not_finite {
102+
Ok(Some(
103+
ConstantArray::new(value.is_sign_positive(), alp.len()).into_array(),
104+
))
105+
} else {
106+
Ok(Some(compare(
107+
alp.encoded(),
108+
&ConstantArray::new(F::encode_below(value, exponents), alp.len()),
109+
// Since the encoded values unencodable lt is equivalent to lte.
110+
// See Gt | Gte for further explanation.
111+
Operator::Lte,
112+
)?))
113+
}
114+
}
92115
},
93116
}
94117
}
95118

96119
#[cfg(test)]
97120
mod tests {
121+
use rstest::rstest;
98122
use vortex_array::ToCanonical;
99123
use vortex_array::arrays::{ConstantArray, PrimitiveArray};
100124
use vortex_array::compute::{Operator, compare};
@@ -274,7 +298,7 @@ mod tests {
274298

275299
#[test]
276300
fn compare_to_null() {
277-
let array = PrimitiveArray::from_iter([1.234f32; 1025]);
301+
let array = PrimitiveArray::from_iter([1.234f32; 10]);
278302
let encoded = alp_encode(&array).unwrap();
279303

280304
let other = ConstantArray::new(
@@ -291,4 +315,32 @@ mod tests {
291315
assert!(!v);
292316
}
293317
}
318+
319+
#[rstest]
320+
#[case(f32::NAN, false)]
321+
#[case(-1.0f32 / 0.0f32, true)]
322+
#[case(f32::INFINITY, false)]
323+
#[case(f32::NEG_INFINITY, true)]
324+
fn compare_to_non_finite_gt(#[case] value: f32, #[case] result: bool) {
325+
let array = PrimitiveArray::from_iter([1.234f32; 10]);
326+
let encoded = alp_encode(&array).unwrap();
327+
328+
let gte = test_alp_compare(&encoded, value, Operator::Gt).unwrap();
329+
330+
assert_eq!(gte, [result; 10]);
331+
}
332+
333+
#[rstest]
334+
#[case(f32::NAN, true)]
335+
#[case(-1.0f32 / 0.0f32, false)]
336+
#[case(f32::INFINITY, true)]
337+
#[case(f32::NEG_INFINITY, false)]
338+
fn compare_to_non_finite_lt(#[case] value: f32, #[case] result: bool) {
339+
let array = PrimitiveArray::from_iter([1.234f32; 10]);
340+
let encoded = alp_encode(&array).unwrap();
341+
342+
let lt = test_alp_compare(&encoded, value, Operator::Lt).unwrap();
343+
344+
assert_eq!(lt, [result; 10]);
345+
}
294346
}

0 commit comments

Comments
 (0)