Skip to main content

bigdecimal/arithmetic/
decimal.rs

1//! Algorithms for manipulating decimal digits
2//!
3//! Note: Many bit-optimizations don't apply when doing decimal
4//!       math, as high-order bits affect low-order decimals
5//!
6
7use crate::*;
8
9/// Shift u32 right by *n* decimal digits
10#[allow(dead_code)]
11pub fn dec_shift_right_u32(x: u32, n: usize) -> u32 {
12    match n {
13        0 => x,
14        1 => x / 10,
15        2 => x / 100,
16        3 => x / 1000,
17        4 => x / 10_000,
18        5 => x / 100_000,
19        6 => x / 1000_000,
20        7 => x / 10_000_000,
21        8 => x / 100_000_000,
22        9 => x / 1000_000_000,
23        _ => 0,
24    }
25}
26
27
28/// Shift u64 right by *n* decimal digits
29#[allow(dead_code)]
30pub fn dec_shift_right_u64(x: u64, n: usize) -> u64 {
31    match n {
32        0 => x,
33        1 => x / 10,
34        2 => x / 100,
35        3 => x / 1000,
36        4 => x / 10_000,
37        5 => x / 100_000,
38        6 => x / 1000_000,
39        7 => x / 10_000_000,
40        8 => x / 100_000_000,
41        9 => x / 1000_000_000,
42        10 => x / 10_000_000_000,
43        11 => x / 100_000_000_000,
44        12 => x / 1000_000_000_000,
45        13 => x / 10_000_000_000_000,
46        14 => x / 100_000_000_000_000,
47        15 => x / 1000_000_000_000_000,
48        16 => x / 10_000_000_000_000_000,
49        17 => x / 100_000_000_000_000_000,
50        18 => x / 1000_000_000_000_000_000,
51        19 => x / 10_000_000_000_000_000_000,
52        _ => 0,
53    }
54}
55
56
57macro_rules! count_digits {
58    ($n:ident : u128) => {
59               if $n >= 100000000000000000000000000000000000000 {
60            39
61        } else if $n >= 10000000000000000000000000000000000000 {
62            38
63        } else if $n >= 1000000000000000000000000000000000000 {
64            37
65        } else if $n >= 100000000000000000000000000000000000 {
66            36
67        } else if $n >= 10000000000000000000000000000000000 {
68            35
69        } else if $n >= 1000000000000000000000000000000000 {
70            34
71        } else if $n >= 100000000000000000000000000000000 {
72            33
73        } else if $n >= 10000000000000000000000000000000 {
74            32
75        } else if $n >= 1000000000000000000000000000000 {
76            31
77        } else if $n >= 100000000000000000000000000000 {
78            30
79        } else if $n >= 10000000000000000000000000000 {
80            29
81        } else if $n >= 1000000000000000000000000000 {
82            28
83        } else if $n >= 100000000000000000000000000 {
84            27
85        } else if $n >= 10000000000000000000000000 {
86            26
87        } else if $n >= 1000000000000000000000000 {
88            25
89        } else if $n >= 100000000000000000000000 {
90            24
91        } else if $n >= 10000000000000000000000 {
92            23
93        } else if $n >= 1000000000000000000000 {
94            22
95        } else if $n >= 100000000000000000000 {
96            21
97        } else {
98            count_digits!($n:u64)
99        }
100    };
101    ($n:ident : u64) => {
102               if $n >= 10000000000000000000 {
103            20
104        } else if $n >= 1000000000000000000 {
105            19
106        } else if $n >= 100000000000000000 {
107            18
108        } else if $n >= 10000000000000000 {
109            17
110        } else if $n >= 1000000000000000 {
111            16
112        } else if $n >= 100000000000000 {
113            15
114        } else if $n >= 10000000000000 {
115            14
116        } else if $n >= 1000000000000 {
117            13
118        } else if $n >= 100000000000 {
119            12
120        } else if $n >= 10000000000 {
121            11
122        } else if $n >= 1000000000 {
123            10
124        } else {
125            count_digits!($n:u32)
126        }
127    };
128    ($n:ident : u32) => {
129               if $n >= 1000000000 {
130            10
131        } else if $n >= 100000000 {
132            9
133        } else if $n >= 10000000 {
134            8
135        } else if $n >= 1000000 {
136            7
137        } else if $n >= 100000 {
138            6
139        } else {
140            count_digits!($n:u16)
141        }
142    };
143    ($n:ident : u16) => {
144               if $n >= 100000 {
145            6
146        } else if $n >= 10000 {
147            5
148        } else if $n >= 1000 {
149            4
150        } else {
151            count_digits!($n:u8)
152        }
153    };
154    ($n:ident : u8) => {
155               if $n >= 100 {
156            3
157        } else if $n >= 10 {
158            2
159        } else {
160            1
161        }
162    };
163}
164
165/// Count digits in u32 (excluding leading-zeros)
166pub(crate) fn count_digits_u32(n: u32) -> usize {
167    if n >= 1000000000 {
    10
} else if n >= 100000000 {
    9
} else if n >= 10000000 {
    8
} else if n >= 1000000 {
    7
} else if n >= 100000 {
    6
} else {
    if n >= 100000 {
        6
    } else if n >= 10000 {
        5
    } else if n >= 1000 {
        4
    } else { if n >= 100 { 3 } else if n >= 10 { 2 } else { 1 } }
}count_digits!(n:u32)
168
169}
170
171/// Count digits in u64 (excluding leading-zeros)
172pub(crate) fn count_digits_u64(n: u64) -> usize {
173    if (n >> 32) == 0 {
174       count_digits_u32(n as u32)
175    } else {
176       if n >= 10000000000000000000 {
    20
} else if n >= 1000000000000000000 {
    19
} else if n >= 100000000000000000 {
    18
} else if n >= 10000000000000000 {
    17
} else if n >= 1000000000000000 {
    16
} else if n >= 100000000000000 {
    15
} else if n >= 10000000000000 {
    14
} else if n >= 1000000000000 {
    13
} else if n >= 100000000000 {
    12
} else if n >= 10000000000 {
    11
} else if n >= 1000000000 {
    10
} else {
    if n >= 1000000000 {
        10
    } else if n >= 100000000 {
        9
    } else if n >= 10000000 {
        8
    } else if n >= 1000000 {
        7
    } else if n >= 100000 {
        6
    } else {
        if n >= 100000 {
            6
        } else if n >= 10000 {
            5
        } else if n >= 1000 {
            4
        } else { if n >= 100 { 3 } else if n >= 10 { 2 } else { 1 } }
    }
}count_digits!(n:u64)
177    }
178}
179
180/// Count digits in u128 (excluding leading-zeros)
181pub(crate) fn count_digits_u128(n: u128) -> usize {
182    if (n >> 64) == 0 {
183       count_digits_u64(n as u64)
184    } else {
185       if n >= 100000000000000000000000000000000000000 {
    39
} else if n >= 10000000000000000000000000000000000000 {
    38
} else if n >= 1000000000000000000000000000000000000 {
    37
} else if n >= 100000000000000000000000000000000000 {
    36
} else if n >= 10000000000000000000000000000000000 {
    35
} else if n >= 1000000000000000000000000000000000 {
    34
} else if n >= 100000000000000000000000000000000 {
    33
} else if n >= 10000000000000000000000000000000 {
    32
} else if n >= 1000000000000000000000000000000 {
    31
} else if n >= 100000000000000000000000000000 {
    30
} else if n >= 10000000000000000000000000000 {
    29
} else if n >= 1000000000000000000000000000 {
    28
} else if n >= 100000000000000000000000000 {
    27
} else if n >= 10000000000000000000000000 {
    26
} else if n >= 1000000000000000000000000 {
    25
} else if n >= 100000000000000000000000 {
    24
} else if n >= 10000000000000000000000 {
    23
} else if n >= 1000000000000000000000 {
    22
} else if n >= 100000000000000000000 {
    21
} else {
    if n >= 10000000000000000000 {
        20
    } else if n >= 1000000000000000000 {
        19
    } else if n >= 100000000000000000 {
        18
    } else if n >= 10000000000000000 {
        17
    } else if n >= 1000000000000000 {
        16
    } else if n >= 100000000000000 {
        15
    } else if n >= 10000000000000 {
        14
    } else if n >= 1000000000000 {
        13
    } else if n >= 100000000000 {
        12
    } else if n >= 10000000000 {
        11
    } else if n >= 1000000000 {
        10
    } else {
        if n >= 1000000000 {
            10
        } else if n >= 100000000 {
            9
        } else if n >= 10000000 {
            8
        } else if n >= 1000000 {
            7
        } else if n >= 100000 {
            6
        } else {
            if n >= 100000 {
                6
            } else if n >= 10000 {
                5
            } else if n >= 1000 {
                4
            } else { if n >= 100 { 3 } else if n >= 10 { 2 } else { 1 } }
        }
    }
}count_digits!(n:u128)
186    }
187}
188
189/// Return number of decimal digits in biginteger
190pub(crate) fn count_digits_bigint(n: &num_bigint::BigInt) -> u64 {
191    count_digits_biguint(n.magnitude())
192}
193
194/// Return number of significant decimal digits in unsigned big-integer
195pub(crate) fn count_digits_biguint(n: &num_bigint::BigUint) -> u64 {
196    use num_traits::ToPrimitive;
197
198    if let Some(n) = n.to_u64() {
199        return count_digits_u64(n) as u64;
200    }
201
202    let mut digits = (n.bits() as f64 / super::LOG2_10) as u64;
203    // guess number of digits based on number of bits in UInt
204    let mut num = super::ten_to_the_uint(digits);
205    if true {
    if !(n * 10u8 >= num) {
        ::core::panicking::panic("assertion failed: n * 10u8 >= num")
    };
};debug_assert!(n * 10u8 >= num);
206
207    while n >= &num {
208        num *= 10u8;
209        digits += 1;
210    }
211    digits
212}
213
214/// convert number of bits to approximate number of digits
215pub(crate) fn bit_to_digit_count(bitcount: u64) -> u64 {
216    (bitcount as f64 / LOG2_10).ceil() as u64
217}
218
219/// convert number of digits to approximate number of bits
220pub(crate) fn digit_to_bit_count(bitcount: u64) -> u64 {
221    (bitcount as f64 * LOG2_10).ceil() as u64
222}
223
224
225/// Return Some(exp) if n == 10^{exp}, otherwise None
226pub(crate) fn get_power_of_ten_u64(n: u64) -> Option<u8> {
227    match n {
228        0 => Some(0),
229        10 => Some(1),
230        100 => Some(2),
231        1000 => Some(3),
232        10000 => Some(4),
233        100000 => Some(5),
234        1000000 => Some(6),
235        10000000 => Some(7),
236        100000000 => Some(8),
237        1000000000 => Some(9),
238        10000000000 => Some(10),
239        n => {
240            let (q, r) = num_integer::div_rem(n, 10000000000);
241            if r == 0 {
242                get_power_of_ten_u64(q).map(|p| p + 10)
243            } else {
244                None
245            }
246        }
247    }
248}
249
250#[cfg(test)]
251mod test {
252    use super::*;
253    include!("decimal.tests.rs");
254}