fixed_num/
i128_ops.rs

1// ================
2// === i128 ops ===
3// ================
4
5pub const POW10: [i128; 39] = {
6    let mut arr = [1i128; 39];
7    let mut val = 1;
8    let mut i = 0;
9    loop {
10        arr[i] = val;
11        if i == 38 { break }
12        val *= 10;
13        i += 1;
14    }
15    arr
16};
17
18pub const P0:  i128 = 1;
19pub const P1:  i128 = P0  * 10;
20pub const P2:  i128 = P1  * 10;
21pub const P3:  i128 = P2  * 10;
22pub const P4:  i128 = P3  * 10;
23pub const P5:  i128 = P4  * 10;
24pub const P6:  i128 = P5  * 10;
25pub const P7:  i128 = P6  * 10;
26pub const P8:  i128 = P7  * 10;
27pub const P9:  i128 = P8  * 10;
28pub const P10: i128 = P9  * 10;
29pub const P11: i128 = P10 * 10;
30pub const P12: i128 = P11 * 10;
31pub const P13: i128 = P12 * 10;
32pub const P14: i128 = P13 * 10;
33pub const P15: i128 = P14 * 10;
34pub const P16: i128 = P15 * 10;
35pub const P17: i128 = P16 * 10;
36pub const P18: i128 = P17 * 10;
37pub const P19: i128 = P18 * 10;
38pub const P20: i128 = P19 * 10;
39pub const P21: i128 = P20 * 10;
40pub const P22: i128 = P21 * 10;
41pub const P23: i128 = P22 * 10;
42pub const P24: i128 = P23 * 10;
43pub const P25: i128 = P24 * 10;
44pub const P26: i128 = P25 * 10;
45pub const P27: i128 = P26 * 10;
46pub const P28: i128 = P27 * 10;
47pub const P29: i128 = P28 * 10;
48pub const P30: i128 = P29 * 10;
49pub const P31: i128 = P30 * 10;
50pub const P32: i128 = P31 * 10;
51pub const P33: i128 = P32 * 10;
52pub const P34: i128 = P33 * 10;
53pub const P35: i128 = P34 * 10;
54pub const P36: i128 = P35 * 10;
55pub const P37: i128 = P36 * 10;
56pub const P38: i128 = P37 * 10;
57
58/// Get the scale factor for rounding to a given number of digits
59#[inline(always)]
60pub(crate) const fn scale_for(digits: i64) -> i128 {
61    let digits = if digits < -19 { -19 } else if digits > 19 { 19 } else { digits };
62    let idx = (19 - digits) as usize;
63    POW10[idx]
64}
65
66/// Returns the number of decimal digits in an `i128`.
67///
68/// This function calculates how many digits are needed to represent the absolute value of the input
69/// number in base 10. The result is always in the range `1..=39`, inclusive. It uses a
70/// fully-unrolled, balanced binary tree of comparisons for maximum performance.
71///
72/// # Tests
73///
74/// ```
75/// use fixed_num::i128_ops::*;
76///
77/// assert_eq!(digit_count(0), 1);
78/// for i in 1..=38 {
79///     let j = i as i32;
80///     assert_eq!(digit_count(POW10[i]-1), j);
81///     assert_eq!(digit_count(POW10[i]),   j + 1);
82/// }
83/// assert_eq!(digit_count(i128::MAX), 39);
84/// assert_eq!(digit_count(i128::MIN), 39);
85/// ```
86#[expect(clippy::cognitive_complexity)]
87#[expect(clippy::collapsible_else_if)]
88#[inline(always)]
89pub const fn digit_count(n: i128) -> i32 {
90    if n == i128::MIN {
91        return 39;
92    }
93    let n = n.abs();
94
95    if n < P19 {
96        if n < P9 {
97            if n < P4 {
98                if n < P2 {
99                    if n < P1 { 1 } else { 2 }
100                } else {
101                    if n < P3 { 3 } else { 4 }
102                }
103            } else {
104                if n < P7 {
105                    if n < P5 { 5 } else if n < P6 { 6 } else { 7 }
106                } else {
107                    if n < P8 { 8 } else { 9 }
108                }
109            }
110        } else {
111            if n < P14 {
112                if n < P12 {
113                    if n < P10 { 10 } else if n < P11 { 11 } else { 12 }
114                } else {
115                    if n < P13 { 13 } else { 14 }
116                }
117            } else {
118                if n < P17 {
119                    if n < P15 { 15 } else if n < P16 { 16 } else { 17 }
120                } else {
121                    if n < P18 { 18 } else { 19 }
122                }
123            }
124        }
125    } else {
126        if n < P29 {
127            if n < P24 {
128                if n < P22 {
129                    if n < P20 { 20 } else if n < P21 { 21 } else { 22 }
130                } else {
131                    if n < P23 { 23 } else { 24 }
132                }
133            } else {
134                if n < P27 {
135                    if n < P25 { 25 } else if n < P26 { 26 } else { 27 }
136                } else {
137                    if n < P28 { 28 } else { 29 }
138                }
139            }
140        } else {
141            if n < P34 {
142                if n < P32 {
143                    if n < P30 { 30 } else if n < P31 { 31 } else { 32 }
144                } else {
145                    if n < P33 { 33 } else { 34 }
146                }
147            } else {
148                if n < P37 {
149                    if n < P35 { 35 } else if n < P36 { 36 } else { 37 }
150                } else {
151                    if n < P38 { 38 } else { 39 }
152                }
153            }
154        }
155    }
156}