jet_proto_math/
number_128.rs

1use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
2
3use bytemuck::{Pod, Zeroable};
4
5const PRECISION: i32 = 10;
6const ONE: i128 = 10_000_000_000;
7
8const POWERS_OF_TEN: &[i128] = &[
9    1,
10    10,
11    100,
12    1_000,
13    10_000,
14    100_000,
15    1_000_000,
16    10_000_000,
17    100_000_000,
18    1_000_000_000,
19    10_000_000_000,
20    100_000_000_000,
21    1_000_000_000_000,
22];
23
24/// A fixed-point decimal number 128 bits wide
25#[derive(Pod, Zeroable, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
26#[repr(C)]
27pub struct Number128(i128);
28
29impl Number128 {
30    pub const ONE: Self = Self(ONE);
31    pub const ZERO: Self = Self(0i128);
32
33    /// Convert this number to fit in a u64
34    ///
35    /// The precision of the number in the u64 is based on the
36    /// exponent provided.
37    pub fn as_u64(&self, exponent: impl Into<i32>) -> u64 {
38        let extra_precision = PRECISION + exponent.into();
39        let prec_value = POWERS_OF_TEN[extra_precision.abs() as usize];
40
41        let target_value = if extra_precision < 0 {
42            self.0 * prec_value
43        } else {
44            self.0 / prec_value
45        };
46
47        if target_value > std::u64::MAX as i128 {
48            panic!("cannot convert to u64 due to overflow");
49        }
50
51        if target_value < 0 {
52            panic!("cannot convert to u64 because value < 0");
53        }
54
55        target_value as u64
56    }
57
58    /// Convert another integer
59    pub fn from_decimal(value: impl Into<i128>, exponent: impl Into<i32>) -> Self {
60        let extra_precision = PRECISION + exponent.into();
61        let prec_value = POWERS_OF_TEN[extra_precision.abs() as usize];
62
63        if extra_precision < 0 {
64            Self(value.into() / prec_value)
65        } else {
66            Self(value.into() * prec_value)
67        }
68    }
69
70    /// Convert from basis points
71    pub fn from_bps(basis_points: u16) -> Self {
72        Self::from_decimal(basis_points, crate::BPS_EXPONENT)
73    }
74
75    /// Get the underlying 128-bit representation in bytes.
76    /// Uses the target endianness of the caller
77    pub fn into_bits(self) -> [u8; 16] {
78        self.0.to_ne_bytes()
79    }
80
81    /// Read a number from a raw 128-bit representation, which was previously
82    /// returned by a call to `into_bits`.
83    /// Uses the target endianness of the caller
84    pub fn from_bits(bits: [u8; 16]) -> Self {
85        Self(i128::from_ne_bytes(bits))
86    }
87
88    /// Get the underlying i128 value
89    pub fn to_i128(self) -> i128 {
90        self.0
91    }
92
93    /// Create `Number128` from an `i128`
94    pub fn from_i128(value: i128) -> Self {
95        Self(value)
96    }
97}
98
99impl std::fmt::Display for Number128 {
100    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101        // todo optimize
102        let rem = self.0 % ONE;
103        let decimal_digits = PRECISION as usize;
104        // convert to abs to remove sign
105        let rem_str = rem.abs().to_string();
106        // regular padding like {:010} doesn't work with i128
107        let decimals = "0".repeat(decimal_digits - rem_str.len()) + &*rem_str;
108        let stripped_decimals = decimals.trim_end_matches('0');
109        let pretty_decimals = if stripped_decimals.is_empty() {
110            "0"
111        } else {
112            stripped_decimals
113        };
114        if self.0 < -ONE {
115            let int = self.0 / ONE;
116            write!(f, "{}.{}", int, pretty_decimals)?;
117        } else if self.0 < 0 {
118            write!(f, "-0.{}", pretty_decimals)?;
119        } else if self.0 < ONE {
120            write!(f, "0.{}", pretty_decimals)?;
121        } else {
122            let int = self.0 / ONE;
123            write!(f, "{}.{}", int, pretty_decimals)?;
124        }
125        Ok(())
126    }
127}
128
129impl Add<Number128> for Number128 {
130    type Output = Self;
131
132    fn add(self, rhs: Number128) -> Self::Output {
133        Self(self.0.checked_add(rhs.0).unwrap())
134    }
135}
136
137impl AddAssign<Number128> for Number128 {
138    fn add_assign(&mut self, rhs: Number128) {
139        self.0 = self.0.checked_add(rhs.0).unwrap();
140    }
141}
142
143impl Sub<Number128> for Number128 {
144    type Output = Self;
145
146    fn sub(self, rhs: Number128) -> Self::Output {
147        Self(self.0.checked_sub(rhs.0).unwrap())
148    }
149}
150
151impl SubAssign<Number128> for Number128 {
152    fn sub_assign(&mut self, rhs: Number128) {
153        self.0 = self.0.checked_sub(rhs.0).unwrap();
154    }
155}
156
157impl Mul<Number128> for Number128 {
158    type Output = Number128;
159
160    fn mul(self, rhs: Number128) -> Self::Output {
161        Self(self.0.checked_mul(rhs.0).unwrap().div(ONE))
162    }
163}
164
165impl MulAssign<Number128> for Number128 {
166    fn mul_assign(&mut self, rhs: Number128) {
167        self.0 = self.0 * rhs.0 / ONE;
168    }
169}
170
171impl Div<Number128> for Number128 {
172    type Output = Number128;
173
174    fn div(self, rhs: Number128) -> Self::Output {
175        Self(self.0.mul(ONE).div(rhs.0))
176    }
177}
178
179impl DivAssign<Number128> for Number128 {
180    fn div_assign(&mut self, rhs: Number128) {
181        self.0 = self.0 * ONE / rhs.0;
182    }
183}
184
185impl<T: Into<i128>> Mul<T> for Number128 {
186    type Output = Number128;
187
188    fn mul(self, rhs: T) -> Self::Output {
189        Self(self.0.mul(rhs.into()))
190    }
191}
192
193impl<T: Into<i128>> Div<T> for Number128 {
194    type Output = Number128;
195
196    fn div(self, rhs: T) -> Self::Output {
197        Self(self.0.div(rhs.into()))
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    #[test]
206    fn zero_equals_zero() {
207        assert_eq!(Number128::ZERO, Number128::from_decimal(0, 0));
208    }
209
210    #[test]
211    fn one_equals_one() {
212        assert_eq!(Number128::ONE, Number128::from_decimal(1, 0));
213    }
214
215    #[test]
216    fn one_plus_one_equals_two() {
217        assert_eq!(
218            Number128::from_decimal(2, 0),
219            Number128::ONE + Number128::ONE
220        );
221    }
222
223    #[test]
224    fn one_minus_one_equals_zero() {
225        assert_eq!(Number128::ONE - Number128::ONE, Number128::ZERO);
226    }
227
228    #[test]
229    fn one_times_one_equals_one() {
230        assert_eq!(Number128::ONE, Number128::ONE * Number128::ONE);
231    }
232
233    #[test]
234    fn one_divided_by_one_equals_one() {
235        assert_eq!(Number128::ONE, Number128::ONE / Number128::ONE);
236    }
237
238    #[test]
239    fn ten_div_100_equals_point_1() {
240        assert_eq!(
241            Number128::from_decimal(1, -1),
242            Number128::from_decimal(1, 1) / Number128::from_decimal(100, 0)
243        );
244    }
245
246    #[test]
247    fn comparison() {
248        let a = Number128::from_decimal(1000, -4);
249        let b = Number128::from_decimal(10, -2);
250        assert!(a >= b);
251
252        let c = Number128::from_decimal(1001, -4);
253        assert!(c > a);
254        assert!(c > b);
255
256        let d = Number128::from_decimal(9999999, -8);
257        assert!(d < a);
258        assert!(d < b);
259        assert!(d < c);
260        assert!(d <= d);
261
262        assert_eq!(a.cmp(&b), std::cmp::Ordering::Equal);
263        assert_eq!(a.cmp(&c), std::cmp::Ordering::Less);
264        assert_eq!(a.cmp(&d), std::cmp::Ordering::Greater);
265    }
266
267    #[test]
268    fn multiply_by_u64() {
269        assert_eq!(
270            Number128::from_decimal(3, 1),
271            Number128::from_decimal(1, 1) * 3u64
272        )
273    }
274
275    #[test]
276    fn test_add_assign_101_2() {
277        let mut a = Number128::from_decimal(101, 0);
278        a += Number128::from_decimal(2, 0);
279        assert_eq!(Number128::from_decimal(103, 0), a);
280    }
281
282    #[test]
283    fn test_sub_assign_101_2() {
284        let mut a = Number128::from_decimal(101, 0);
285        a -= Number128::from_decimal(2, 0);
286        assert_eq!(Number128::from_decimal(99, 0), a);
287    }
288
289    #[test]
290    fn test_mul_assign_101_2() {
291        let mut a = Number128::from_decimal(101, 0);
292        a *= Number128::from_decimal(2, 0);
293        assert_eq!(Number128::from_decimal(202, 0).0, a.0);
294    }
295
296    #[test]
297    fn test_div_assign_101_2() {
298        let mut a = Number128::from_decimal(101, 0);
299        a /= Number128::from_decimal(2, 0);
300        assert_eq!(Number128::from_decimal(505, -1), a);
301    }
302
303    #[test]
304    fn test_div_assign_102_3() {
305        let mut a = Number128::from_decimal(1, 1);
306        a /= Number128::from_decimal(100, 0);
307        assert_eq!(Number128::from_decimal(1, -1).0, a.0);
308    }
309
310    #[test]
311    fn div_into_i128() {
312        let a = Number128::from_decimal(1000, 0);
313        let b = a / 500;
314        assert_eq!(Number128::from_decimal(2, 0), b);
315
316        let c = Number128::from_decimal(1000, -3);
317        let d = c / 3;
318        assert_eq!(Number128::from_decimal(3333333333i64, -10).0, d.0);
319    }
320
321    #[test]
322    fn equality() {
323        let a = Number128::from_decimal(1000, -4);
324        let b = Number128::from_decimal(10, -2);
325        assert_eq!(a, b);
326
327        let c = Number128::from_decimal(-1000, -4);
328        assert_ne!(a, c);
329        assert_ne!(b, c);
330    }
331
332    #[test]
333    fn as_u64() {
334        let u64in = 31455;
335        let a = Number128::from_decimal(u64in, -3);
336        let b = a.as_u64(-3);
337        assert_eq!(b, u64in);
338    }
339
340    #[test]
341    #[should_panic = "cannot convert to u64 because value < 0"]
342    fn as_u64_panic_neg() {
343        let a = Number128::from_decimal(-10000, -3);
344        a.as_u64(-3);
345    }
346
347    #[test]
348    #[should_panic = "cannot convert to u64 due to overflow"]
349    fn as_u64_panic_big() {
350        let a = Number128::from_decimal(u64::MAX as i128 + 1, -3);
351        a.as_u64(-3);
352    }
353
354    #[test]
355    fn display() {
356        let a = Number128::from_bps(15000);
357        assert_eq!("1.5", a.to_string().as_str());
358
359        let a = Number128::from_bps(0) - Number128::from_bps(15000);
360        assert_eq!("-1.5", a.to_string().as_str());
361
362        let b = Number128::from_decimal(12345678901i128, -10);
363        assert_eq!("1.2345678901", b.to_string().as_str());
364
365        let b = Number128::from_decimal(-12345678901i128, -10);
366        assert_eq!("-1.2345678901", b.to_string().as_str());
367
368        let c = Number128::from_decimal(-12345678901i128, -9);
369        assert_eq!("-12.345678901", c.to_string().as_str());
370
371        let c = Number128::from_decimal(12345678901i128, -9);
372        assert_eq!("12.345678901", c.to_string().as_str());
373
374        let d = Number128::from_decimal(ONE - 1, 1);
375        assert_eq!("99999999990.0", d.to_string().as_str());
376
377        let e = Number128::from_decimal(12345678901i128, -13);
378        assert_eq!("0.0012345678", e.to_string().as_str());
379
380        let e = Number128::from_decimal(-12345678901i128, -13);
381        assert_eq!("-0.0012345678", e.to_string().as_str());
382    }
383
384    #[test]
385    fn into_bits() {
386        let bits = Number128::from_decimal(1242, -3).into_bits();
387        let number = Number128::from_bits(bits);
388
389        assert_eq!(Number128::from_decimal(1242, -3), number);
390    }
391}