diff --git a/datafusion/common/src/rounding.rs b/datafusion/common/src/rounding.rs index 413067ecd61ed..95eefd3235b5f 100644 --- a/datafusion/common/src/rounding.rs +++ b/datafusion/common/src/rounding.rs @@ -77,6 +77,7 @@ pub trait FloatBits { /// The integer value 0, used in bitwise operations. const ZERO: Self::Item; + const NEG_ZERO: Self::Item; /// Converts the floating-point value to its bitwise representation. fn to_bits(self) -> Self::Item; @@ -101,6 +102,7 @@ impl FloatBits for f32 { const CLEAR_SIGN_MASK: u32 = 0x7fff_ffff; const ONE: Self::Item = 1; const ZERO: Self::Item = 0; + const NEG_ZERO: Self::Item = 0x8000_0000; fn to_bits(self) -> Self::Item { self.to_bits() @@ -130,6 +132,7 @@ impl FloatBits for f64 { const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff; const ONE: Self::Item = 1; const ZERO: Self::Item = 0; + const NEG_ZERO: Self::Item = 0x8000_0000_0000_0000; fn to_bits(self) -> Self::Item { self.to_bits() @@ -175,8 +178,10 @@ pub fn next_up(float: F) -> F { } let abs = bits & F::CLEAR_SIGN_MASK; - let next_bits = if abs == F::ZERO { + let next_bits = if bits == F::ZERO { F::TINY_BITS + } else if abs == F::ZERO { + F::ZERO } else if bits == abs { bits + F::ONE } else { @@ -206,8 +211,11 @@ pub fn next_down(float: F) -> F { if float.float_is_nan() || bits == F::neg_infinity().to_bits() { return float; } + let abs = bits & F::CLEAR_SIGN_MASK; - let next_bits = if abs == F::ZERO { + let next_bits = if bits == F::ZERO { + F::NEG_ZERO + } else if abs == F::ZERO { F::NEG_TINY_BITS } else if bits == abs { bits - F::ONE @@ -396,4 +404,32 @@ mod tests { let result = next_down(value); assert!(result.is_nan()); } + + #[test] + fn test_next_up_neg_zero_f32() { + let value: f32 = -0.0; + let result = next_up(value); + assert_eq!(result, 0.0); + } + + #[test] + fn test_next_down_zero_f32() { + let value: f32 = 0.0; + let result = next_down(value); + assert_eq!(result, -0.0); + } + + #[test] + fn test_next_up_neg_zero_f64() { + let value: f64 = -0.0; + let result = next_up(value); + assert_eq!(result, 0.0); + } + + #[test] + fn test_next_down_zero_f64() { + let value: f64 = 0.0; + let result = next_down(value); + assert_eq!(result, -0.0); + } } diff --git a/datafusion/expr-common/src/interval_arithmetic.rs b/datafusion/expr-common/src/interval_arithmetic.rs index 16a8caad823e2..b5b632076b006 100644 --- a/datafusion/expr-common/src/interval_arithmetic.rs +++ b/datafusion/expr-common/src/interval_arithmetic.rs @@ -3698,6 +3698,76 @@ mod tests { Interval::make(Some(-500.0_f64), Some(1000.0_f64))?, Interval::make(Some(-500.0_f64), Some(500.0_f64))?, ), + ( + Interval::make(Some(0_i64), Some(0_i64))?, + Interval::make(Some(-0_i64), Some(0_i64))?, + true, + Interval::make(Some(0_i64), Some(0_i64))?, + Interval::make(Some(-0_i64), Some(0_i64))?, + ), + ( + Interval::make(Some(-0_i64), Some(0_i64))?, + Interval::make(Some(-0_i64), Some(-0_i64))?, + true, + Interval::make(Some(-0_i64), Some(0_i64))?, + Interval::make(Some(-0_i64), Some(-0_i64))?, + ), + ( + Interval::make(Some(0.0_f64), Some(0.0_f64))?, + Interval::make(Some(-0.0_f64), Some(0.0_f64))?, + true, + Interval::make(Some(0.0_f64), Some(0.0_f64))?, + Interval::make(Some(-0.0_f64), Some(0.0_f64))?, + ), + ( + Interval::make(Some(0.0_f64), Some(0.0_f64))?, + Interval::make(Some(-0.0_f64), Some(0.0_f64))?, + false, + Interval::make(Some(0.0_f64), Some(0.0_f64))?, + Interval::make(Some(-0.0_f64), Some(-0.0_f64))?, + ), + ( + Interval::make(Some(-0.0_f64), Some(0.0_f64))?, + Interval::make(Some(-0.0_f64), Some(-0.0_f64))?, + true, + Interval::make(Some(-0.0_f64), Some(0.0_f64))?, + Interval::make(Some(-0.0_f64), Some(-0.0_f64))?, + ), + ( + Interval::make(Some(-0.0_f64), Some(0.0_f64))?, + Interval::make(Some(-0.0_f64), Some(-0.0_f64))?, + false, + Interval::make(Some(0.0_f64), Some(0.0_f64))?, + Interval::make(Some(-0.0_f64), Some(-0.0_f64))?, + ), + ( + Interval::make(Some(0_i64), None)?, + Interval::make(Some(-0_i64), None)?, + true, + Interval::make(Some(0_i64), None)?, + Interval::make(Some(-0_i64), None)?, + ), + ( + Interval::make(Some(0_i64), None)?, + Interval::make(Some(-0_i64), None)?, + false, + Interval::make(Some(1_i64), None)?, + Interval::make(Some(-0_i64), None)?, + ), + ( + Interval::make(Some(0.0_f64), None)?, + Interval::make(Some(-0.0_f64), None)?, + true, + Interval::make(Some(0.0_f64), None)?, + Interval::make(Some(-0.0_f64), None)?, + ), + ( + Interval::make(Some(0.0_f64), None)?, + Interval::make(Some(-0.0_f64), None)?, + false, + Interval::make(Some(0.0_f64), None)?, + Interval::make(Some(-0.0_f64), None)?, + ), ]; for (first, second, includes_endpoints, left_modified, right_modified) in cases { assert_eq!( @@ -3717,6 +3787,16 @@ mod tests { Interval::make(Some(1500.0_f32), Some(2000.0_f32))?, false, ), + ( + Interval::make(Some(0_i64), Some(0_i64))?, + Interval::make(Some(-0_i64), Some(0_i64))?, + false, + ), + ( + Interval::make(Some(-0_i64), Some(0_i64))?, + Interval::make(Some(-0_i64), Some(-0_i64))?, + false, + ), ]; for (first, second, includes_endpoints) in infeasible_cases { assert_eq!(satisfy_greater(&first, &second, !includes_endpoints)?, None);