rust_decimal/ops/
cmp.rs

1use crate::constants::{MAX_I32_SCALE, POWERS_10, U32_MASK, U32_MAX};
2use crate::decimal::Decimal;
3use crate::ops::common::Dec64;
4
5use core::cmp::Ordering;
6
7pub(crate) fn cmp_impl(d1: &Decimal, d2: &Decimal) -> Ordering {
8    if d2.is_zero() {
9        return if d1.is_zero() {
10            return Ordering::Equal;
11        } else if d1.is_sign_negative() {
12            Ordering::Less
13        } else {
14            Ordering::Greater
15        };
16    }
17    if d1.is_zero() {
18        return if d2.is_sign_negative() {
19            Ordering::Greater
20        } else {
21            Ordering::Less
22        };
23    }
24    // If the sign is different, then it's an easy answer
25    if d1.is_sign_negative() != d2.is_sign_negative() {
26        return if d1.is_sign_negative() {
27            Ordering::Less
28        } else {
29            Ordering::Greater
30        };
31    }
32
33    // Otherwise, do a deep comparison
34    let d1 = Dec64::new(d1);
35    let d2 = Dec64::new(d2);
36    // We know both signs are the same here so flip it here.
37    // Negative is handled differently. i.e. 0.5 > 0.01 however -0.5 < -0.01
38    if d1.negative {
39        cmp_internal(&d2, &d1)
40    } else {
41        cmp_internal(&d1, &d2)
42    }
43}
44
45pub(in crate::ops) fn cmp_internal(d1: &Dec64, d2: &Dec64) -> Ordering {
46    // This function ignores sign
47    let mut d1_low = d1.low64;
48    let mut d1_high = d1.hi;
49    let mut d2_low = d2.low64;
50    let mut d2_high = d2.hi;
51
52    // If the scale factors aren't equal then
53    if d1.scale != d2.scale {
54        let mut diff = d2.scale as i32 - d1.scale as i32;
55        if diff < 0 {
56            diff = -diff;
57            if !rescale(&mut d2_low, &mut d2_high, diff as u32) {
58                return Ordering::Less;
59            }
60        } else if !rescale(&mut d1_low, &mut d1_high, diff as u32) {
61            return Ordering::Greater;
62        }
63    }
64
65    // They're the same scale, do a standard bitwise comparison
66    let hi_order = d1_high.cmp(&d2_high);
67    if hi_order != Ordering::Equal {
68        return hi_order;
69    }
70    d1_low.cmp(&d2_low)
71}
72
73fn rescale(low64: &mut u64, high: &mut u32, diff: u32) -> bool {
74    let mut diff = diff as i32;
75    // We need to modify d1 by 10^diff to get it to the same scale as d2
76    loop {
77        let power = if diff >= MAX_I32_SCALE {
78            POWERS_10[9]
79        } else {
80            POWERS_10[diff as usize]
81        } as u64;
82        let tmp_lo_32 = (*low64 & U32_MASK) * power;
83        let mut tmp = (*low64 >> 32) * power + (tmp_lo_32 >> 32);
84        *low64 = (tmp_lo_32 & U32_MASK) + (tmp << 32);
85        tmp >>= 32;
86        tmp = tmp.wrapping_add((*high as u64) * power);
87        // Indicates > 96 bits
88        if tmp > U32_MAX {
89            return false;
90        }
91        *high = tmp as u32;
92
93        // Keep scaling if there is more to go
94        diff -= MAX_I32_SCALE;
95        if diff <= 0 {
96            break;
97        }
98    }
99
100    true
101}