This is an automated email from the ASF dual-hosted git repository.

liurenjie1024 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-rust.git


The following commit(s) were added to refs/heads/main by this push:
     new 99ca196a7 fix: follow IEEE 754 totalOrder for `float` and `double` 
(#1959)
99ca196a7 is described below

commit 99ca196a7a81343ac35d993d9db2ec59c626f389
Author: Alan Tang <[email protected]>
AuthorDate: Thu Dec 25 09:03:32 2025 +0800

    fix: follow IEEE 754 totalOrder for `float` and `double` (#1959)
    
    ## Which issue does this PR close?
    
    - Closes #1951.
    
    ## What changes are included in this PR?
    
    
    For `F32` and `F64`, we use the `total_cmp` method for comparison.
    
    ## Are these changes tested?
    
    
    Yes, I added test cases to verify whether the comparison follows the
    IEEE 754 rules.
    
    Signed-off-by: StandingMan <[email protected]>
    Co-authored-by: Renjie Liu <[email protected]>
---
 crates/iceberg/src/spec/values/datum.rs | 36 ++++++++-------------------------
 crates/iceberg/src/spec/values/tests.rs | 25 +++++++++++++++++++++++
 2 files changed, 33 insertions(+), 28 deletions(-)

diff --git a/crates/iceberg/src/spec/values/datum.rs 
b/crates/iceberg/src/spec/values/datum.rs
index cb60fb94e..88209ae95 100644
--- a/crates/iceberg/src/spec/values/datum.rs
+++ b/crates/iceberg/src/spec/values/datum.rs
@@ -166,36 +166,16 @@ impl<'de> Deserialize<'de> for Datum {
 
 // Compare following iceberg float ordering rules:
 //  -NaN < -Infinity < -value < -0 < 0 < value < Infinity < NaN
-fn iceberg_float_cmp<T: Float>(a: T, b: T) -> Option<Ordering> {
-    if a.is_nan() && b.is_nan() {
-        return match (a.is_sign_negative(), b.is_sign_negative()) {
-            (true, false) => Some(Ordering::Less),
-            (false, true) => Some(Ordering::Greater),
-            _ => Some(Ordering::Equal),
-        };
-    }
-
-    if a.is_nan() {
-        return Some(if a.is_sign_negative() {
-            Ordering::Less
-        } else {
-            Ordering::Greater
-        });
-    }
-
-    if b.is_nan() {
-        return Some(if b.is_sign_negative() {
-            Ordering::Greater
-        } else {
-            Ordering::Less
-        });
-    }
+fn iceberg_float_cmp_f32(a: OrderedFloat<f32>, b: OrderedFloat<f32>) -> 
Option<Ordering> {
+    Some(a.total_cmp(&b))
+}
 
-    a.partial_cmp(&b)
+fn iceberg_float_cmp_f64(a: OrderedFloat<f64>, b: OrderedFloat<f64>) -> 
Option<Ordering> {
+    Some(a.total_cmp(&b))
 }
 
 impl PartialOrd for Datum {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
         match (&self.literal, &other.literal, &self.r#type, &other.r#type) {
             // generate the arm with same type and same literal
             (
@@ -221,13 +201,13 @@ impl PartialOrd for Datum {
                 PrimitiveLiteral::Float(other_val),
                 PrimitiveType::Float,
                 PrimitiveType::Float,
-            ) => iceberg_float_cmp(*val, *other_val),
+            ) => iceberg_float_cmp_f32(*val, *other_val),
             (
                 PrimitiveLiteral::Double(val),
                 PrimitiveLiteral::Double(other_val),
                 PrimitiveType::Double,
                 PrimitiveType::Double,
-            ) => iceberg_float_cmp(*val, *other_val),
+            ) => iceberg_float_cmp_f64(*val, *other_val),
             (
                 PrimitiveLiteral::Int(val),
                 PrimitiveLiteral::Int(other_val),
diff --git a/crates/iceberg/src/spec/values/tests.rs 
b/crates/iceberg/src/spec/values/tests.rs
index 73343a9a1..bb10701d8 100644
--- a/crates/iceberg/src/spec/values/tests.rs
+++ b/crates/iceberg/src/spec/values/tests.rs
@@ -1293,6 +1293,31 @@ fn test_iceberg_float_order() {
     assert_eq!(double_sorted, double_expected);
 }
 
+#[test]
+fn test_negative_zero_less_than_positive_zero() {
+    {
+        let neg_zero = Datum::float(-0.0);
+        let pos_zero = Datum::float(0.0);
+
+        assert_eq!(
+            neg_zero.partial_cmp(&pos_zero),
+            Some(std::cmp::Ordering::Less),
+            "IEEE 754 totalOrder requires -0.0 < +0.0 on F32"
+        );
+    }
+
+    {
+        let neg_zero = Datum::double(-0.0);
+        let pos_zero = Datum::double(0.0);
+
+        assert_eq!(
+            neg_zero.partial_cmp(&pos_zero),
+            Some(std::cmp::Ordering::Less),
+            "IEEE 754 totalOrder requires -0.0 < +0.0 on F64"
+        );
+    }
+}
+
 /// Test Date deserialization from JSON as number (days since epoch).
 ///
 /// This reproduces the scenario from Iceberg Java's TestAddFilesProcedure 
where:

Reply via email to