df_hyperliquid_core/utils/dec/
mod.rs1mod arith;
5mod round;
6mod wire;
7
8pub use round::Round;
9pub use wire::BadDec;
10
11use std::cmp::Ordering;
12
13pub const MAX_SCALE: u32 = 18;
16
17#[derive(Debug, Clone, Copy, Default)]
21pub struct Dec {
22 units: i128,
23 scale: u32,
24}
25
26impl Dec {
27 pub const ZERO: Dec = Dec { units: 0, scale: 0 };
28 pub const ONE: Dec = Dec { units: 1, scale: 0 };
29
30 #[must_use]
32 pub const fn new(units: i128, scale: u32) -> Option<Dec> {
33 if scale > MAX_SCALE {
34 return None;
35 }
36 Some(Dec { units, scale })
37 }
38
39 #[must_use]
47 pub const fn lit<const SCALE: u32>(units: i128) -> Dec {
48 const { assert!(SCALE <= MAX_SCALE, "a Dec keeps at most eighteen places") };
49 Dec { units, scale: SCALE }
50 }
51
52 #[must_use]
53 pub const fn from_int(whole: i64) -> Dec {
54 Dec { units: whole as i128, scale: 0 }
55 }
56
57 #[must_use]
59 fn normal(mut self) -> Dec {
60 while self.scale > 0 && self.units % 10 == 0 {
61 self.units /= 10;
62 self.scale -= 1;
63 }
64 if self.units == 0 {
65 self.scale = 0;
66 }
67 self
68 }
69
70 #[must_use]
71 pub const fn is_zero(self) -> bool {
72 self.units == 0
73 }
74
75 #[must_use]
76 pub const fn is_negative(self) -> bool {
77 self.units < 0
78 }
79
80 #[must_use]
81 pub const fn is_positive(self) -> bool {
82 self.units > 0
83 }
84
85 #[must_use]
86 pub fn is_integer(self) -> bool {
87 self.normal().scale == 0
88 }
89
90 #[must_use]
92 pub fn places(self) -> u32 {
93 self.normal().scale
94 }
95
96 #[must_use]
97 pub const fn abs(self) -> Dec {
98 Dec { units: self.units.abs(), scale: self.scale }
99 }
100
101 #[must_use]
102 pub const fn negate(self) -> Dec {
103 Dec { units: -self.units, scale: self.scale }
104 }
105
106 fn aligned(self, other: Dec) -> Option<(i128, i128, u32)> {
107 let scale = self.scale.max(other.scale);
108 Some((
109 self.units.checked_mul(ten(scale - self.scale)?)?,
110 other.units.checked_mul(ten(scale - other.scale)?)?,
111 scale,
112 ))
113 }
114}
115
116fn ten(power: u32) -> Option<i128> {
118 10i128.checked_pow(power)
119}
120
121impl PartialEq for Dec {
122 fn eq(&self, other: &Dec) -> bool {
123 self.cmp(other) == Ordering::Equal
124 }
125}
126
127impl Eq for Dec {}
128
129impl PartialOrd for Dec {
130 fn partial_cmp(&self, other: &Dec) -> Option<Ordering> {
131 Some(self.cmp(other))
132 }
133}
134
135impl Ord for Dec {
136 fn cmp(&self, other: &Dec) -> Ordering {
137 match self.aligned(*other) {
138 Some((a, b, _)) => a.cmp(&b),
139 None => (self.units / ten(self.scale).unwrap_or(1)).cmp(&(other.units / ten(other.scale).unwrap_or(1))),
142 }
143 }
144}
145
146#[cfg(test)]
147#[path = "tests/dec.rs"]
148mod tests;