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

tustvold pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/master by this push:
     new ed9fc565f fix: use signed comparator to compare decimal128 and 
decimal256 (#2275)
ed9fc565f is described below

commit ed9fc565f6b3bf0653ff342b523dbd1e2192d847
Author: Kun Liu <[email protected]>
AuthorDate: Tue Aug 2 18:22:13 2022 +0800

    fix: use signed comparator to compare decimal128 and decimal256 (#2275)
    
    * fix bug: decimal cmp
    
    * optimizer the error message
    
    * address comment
---
 arrow/src/util/decimal.rs | 116 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 113 insertions(+), 3 deletions(-)

diff --git a/arrow/src/util/decimal.rs b/arrow/src/util/decimal.rs
index 8f9d394ef..810581591 100644
--- a/arrow/src/util/decimal.rs
+++ b/arrow/src/util/decimal.rs
@@ -215,7 +215,7 @@ macro_rules! def_decimal {
                     "Cannot compare two Decimals with different scale: {}, {}",
                     self.scale, other.scale
                 );
-                self.value.partial_cmp(&other.value)
+                Some(singed_cmp_le_bytes(&self.value, &other.value))
             }
         }
 
@@ -226,7 +226,7 @@ macro_rules! def_decimal {
                     "Cannot compare two Decimals with different scale: {}, {}",
                     self.scale, other.scale
                 );
-                self.value.cmp(&other.value)
+                singed_cmp_le_bytes(&self.value, &other.value)
             }
         }
 
@@ -245,6 +245,49 @@ macro_rules! def_decimal {
     };
 }
 
+// compare two signed integer which are encoded with little endian.
+// left bytes and right bytes must have the same length.
+fn singed_cmp_le_bytes(left: &[u8], right: &[u8]) -> Ordering {
+    assert_eq!(
+        left.len(),
+        right.len(),
+        "Can't compare bytes array with different len: {}, {}",
+        left.len(),
+        right.len()
+    );
+    assert_ne!(left.len(), 0, "Can't compare bytes array of length 0");
+    let len = left.len();
+    // the sign bit is 1, the value is negative
+    let left_negative = left[len - 1] >= 0x80_u8;
+    let right_negative = right[len - 1] >= 0x80_u8;
+    if left_negative != right_negative {
+        return match left_negative {
+            true => {
+                // left is negative value
+                // right is positive value
+                Ordering::Less
+            }
+            false => {
+                Ordering::Greater
+            }
+        };
+    }
+    for i in 0..len {
+        let l_byte = left[len - 1 - i];
+        let r_byte = right[len - 1 - i];
+        match l_byte.cmp(&r_byte) {
+            Ordering::Less => {
+                return Ordering::Less;
+            }
+            Ordering::Greater => {
+                return Ordering::Greater;
+            }
+            Ordering::Equal => {}
+        }
+    }
+    Ordering::Equal
+}
+
 def_decimal!(
     Decimal128,
     128,
@@ -260,8 +303,11 @@ def_decimal!(
 
 #[cfg(test)]
 mod tests {
-    use crate::util::decimal::{BasicDecimal, Decimal128, Decimal256};
+    use crate::util::decimal::{
+        singed_cmp_le_bytes, BasicDecimal, Decimal128, Decimal256,
+    };
     use num::{BigInt, Num};
+    use rand::random;
 
     #[test]
     fn decimal_128_to_string() {
@@ -368,4 +414,68 @@ mod tests {
         let value = Decimal256::from_big_int(&num, 76, 4).unwrap();
         assert_eq!(value.to_string(), 
"-574437317700748313234121683441537667865831564552201235664496608164256541.5731");
     }
+
+    #[test]
+    fn test_lt_cmp_byte() {
+        for _i in 0..100 {
+            let left = random::<i128>();
+            let right = random::<i128>();
+            let result = singed_cmp_le_bytes(
+                left.to_le_bytes().as_slice(),
+                right.to_le_bytes().as_slice(),
+            );
+            assert_eq!(left.cmp(&right), result);
+        }
+        for _i in 0..100 {
+            let left = random::<i32>();
+            let right = random::<i32>();
+            let result = singed_cmp_le_bytes(
+                left.to_le_bytes().as_slice(),
+                right.to_le_bytes().as_slice(),
+            );
+            assert_eq!(left.cmp(&right), result);
+        }
+    }
+
+    #[test]
+    fn compare_decimal128() {
+        let v1 = -100_i128;
+        let v2 = 10000_i128;
+        let right = Decimal128::new_from_i128(20, 3, v2);
+        for v in v1..v2 {
+            let left = Decimal128::new_from_i128(20, 3, v);
+            assert!(left < right);
+        }
+
+        for _i in 0..100 {
+            let left = random::<i128>();
+            let right = random::<i128>();
+            let left_decimal = Decimal128::new_from_i128(38, 2, left);
+            let right_decimal = Decimal128::new_from_i128(38, 2, right);
+            assert_eq!(left < right, left_decimal < right_decimal);
+            assert_eq!(left == right, left_decimal == right_decimal)
+        }
+    }
+
+    #[test]
+    fn compare_decimal256() {
+        let v1 = -100_i128;
+        let v2 = 10000_i128;
+        let right = Decimal256::from_big_int(&BigInt::from(v2), 75, 
2).unwrap();
+        for v in v1..v2 {
+            let left = Decimal256::from_big_int(&BigInt::from(v), 75, 
2).unwrap();
+            assert!(left < right);
+        }
+
+        for _i in 0..100 {
+            let left = random::<i128>();
+            let right = random::<i128>();
+            let left_decimal =
+                Decimal256::from_big_int(&BigInt::from(left), 75, 2).unwrap();
+            let right_decimal =
+                Decimal256::from_big_int(&BigInt::from(right), 75, 2).unwrap();
+            assert_eq!(left < right, left_decimal < right_decimal);
+            assert_eq!(left == right, left_decimal == right_decimal)
+        }
+    }
 }

Reply via email to