summaryrefslogtreecommitdiffstats
path: root/third_party/rust/rust_decimal/src/ops/cmp.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/rust_decimal/src/ops/cmp.rs')
-rw-r--r--third_party/rust/rust_decimal/src/ops/cmp.rs101
1 files changed, 101 insertions, 0 deletions
diff --git a/third_party/rust/rust_decimal/src/ops/cmp.rs b/third_party/rust/rust_decimal/src/ops/cmp.rs
new file mode 100644
index 0000000000..636085bff5
--- /dev/null
+++ b/third_party/rust/rust_decimal/src/ops/cmp.rs
@@ -0,0 +1,101 @@
+use crate::constants::{MAX_I32_SCALE, POWERS_10, U32_MASK, U32_MAX};
+use crate::decimal::Decimal;
+use crate::ops::common::Dec64;
+
+use core::cmp::Ordering;
+
+pub(crate) fn cmp_impl(d1: &Decimal, d2: &Decimal) -> Ordering {
+ if d2.is_zero() {
+ return if d1.is_zero() {
+ return Ordering::Equal;
+ } else if d1.is_sign_negative() {
+ Ordering::Less
+ } else {
+ Ordering::Greater
+ };
+ }
+ if d1.is_zero() {
+ return if d2.is_sign_negative() {
+ Ordering::Greater
+ } else {
+ Ordering::Less
+ };
+ }
+ // If the sign is different, then it's an easy answer
+ if d1.is_sign_negative() != d2.is_sign_negative() {
+ return if d1.is_sign_negative() {
+ Ordering::Less
+ } else {
+ Ordering::Greater
+ };
+ }
+
+ // Otherwise, do a deep comparison
+ let d1 = Dec64::new(d1);
+ let d2 = Dec64::new(d2);
+ // We know both signs are the same here so flip it here.
+ // Negative is handled differently. i.e. 0.5 > 0.01 however -0.5 < -0.01
+ if d1.negative {
+ cmp_internal(&d2, &d1)
+ } else {
+ cmp_internal(&d1, &d2)
+ }
+}
+
+pub(in crate::ops) fn cmp_internal(d1: &Dec64, d2: &Dec64) -> Ordering {
+ // This function ignores sign
+ let mut d1_low = d1.low64;
+ let mut d1_high = d1.hi;
+ let mut d2_low = d2.low64;
+ let mut d2_high = d2.hi;
+
+ // If the scale factors aren't equal then
+ if d1.scale != d2.scale {
+ let mut diff = d2.scale as i32 - d1.scale as i32;
+ if diff < 0 {
+ diff = -diff;
+ if !rescale(&mut d2_low, &mut d2_high, diff as u32) {
+ return Ordering::Less;
+ }
+ } else if !rescale(&mut d1_low, &mut d1_high, diff as u32) {
+ return Ordering::Greater;
+ }
+ }
+
+ // They're the same scale, do a standard bitwise comparison
+ let hi_order = d1_high.cmp(&d2_high);
+ if hi_order != Ordering::Equal {
+ return hi_order;
+ }
+ d1_low.cmp(&d2_low)
+}
+
+fn rescale(low64: &mut u64, high: &mut u32, diff: u32) -> bool {
+ let mut diff = diff as i32;
+ // We need to modify d1 by 10^diff to get it to the same scale as d2
+ loop {
+ let power = if diff >= MAX_I32_SCALE {
+ POWERS_10[9]
+ } else {
+ POWERS_10[diff as usize]
+ } as u64;
+ let tmp_lo_32 = (*low64 & U32_MASK) * power;
+ let mut tmp = (*low64 >> 32) * power + (tmp_lo_32 >> 32);
+ *low64 = (tmp_lo_32 & U32_MASK) + (tmp << 32);
+ tmp >>= 32;
+ tmp = tmp.wrapping_add((*high as u64) * power);
+ // Indicates > 96 bits
+ if tmp > U32_MAX {
+ return false;
+ }
+ *high = tmp as u32;
+
+ // Keep scaling if there is more to go
+ diff -= MAX_I32_SCALE;
+ if diff <= 0 {
+ break;
+ }
+ }
+
+ true
+}