1use crate::utils::get_units;
2use std::hash::{Hash, Hasher};
3use crate::error::{Result, Error};
4
5pub const MAX_UNITS: usize = 9;
6pub const MAX_FRAC: usize = 30;
7pub const MAX_DIGITS: usize = 65;
8pub const DIGITS_PER_UNIT: usize = 9;
9pub const UNIT: i32 = 1_000_000_000;
10pub const HALF_UNIT: i32 = UNIT / 2;
11pub const MAX_FRAC_UNITS: usize = (MAX_FRAC + DIGITS_PER_UNIT - 1) / DIGITS_PER_UNIT;
12pub const MAX_I64: FixedDecimal = FixedDecimal {
13 intg: 19 as i8,
14 frac: 0,
15 lsu: [854775807, 223372036, 9, 0, 0, 0, 0, 0, 0],
16};
17pub const MIN_I64: FixedDecimal = FixedDecimal {
18 intg: (19 | 0x80) as i8,
19 frac: 0,
20 lsu: [854775808, 223372036, 9, 0, 0, 0, 0, 0, 0],
21};
22pub const MAX_U64: FixedDecimal = FixedDecimal {
23 intg: 20 as i8,
24 frac: 0,
25 lsu: [709551615, 446744073, 18, 0, 0, 0, 0, 0, 0],
26};
27
28#[derive(Debug, Clone)]
33pub struct FixedDecimal {
34 pub(crate) intg: i8,
38 pub(crate) frac: i8,
41 pub(crate) lsu: [i32; MAX_UNITS],
42}
43
44impl FixedDecimal {
45 #[inline]
46 pub fn zero() -> FixedDecimal {
47 FixedDecimal {
48 intg: 1,
49 frac: 0,
50 lsu: [0; MAX_UNITS],
51 }
52 }
53
54 #[inline]
55 pub fn one() -> FixedDecimal {
56 let mut fd = Self::zero();
57 fd.lsu[0] = 1;
58 fd
59 }
60
61 #[inline]
62 pub fn is_neg(&self) -> bool {
63 self.intg & (0x80u8 as i8) != 0
64 }
65
66 #[inline]
67 pub fn set_neg(&mut self) {
68 self.intg |= 0x80u8 as i8;
69 }
70
71 #[inline]
72 pub(crate) fn set_neg_and_check_zero(&mut self) {
73 self.set_neg();
74 if self.is_zero() {
75 self.set_pos();
76 }
77 }
78
79 #[inline]
80 pub fn set_pos(&mut self) {
81 self.intg &= 0x7f;
82 }
83
84 #[inline]
85 pub fn set_zero(&mut self) {
86 self.intg = 1;
87 self.frac = 0;
88 self.reset_units();
89 }
90
91 #[inline]
92 pub fn is_zero(&self) -> bool {
93 self.lsu.iter().all(|n| *n == 0)
94 }
95
96 #[inline]
97 pub fn set_one(&mut self) {
98 self.intg = 1;
99 self.frac = 0;
100 self.reset_units();
101 self.lsu[0] = 1;
102 }
103
104 #[inline]
105 pub fn intg(&self) -> i8 {
106 self.intg & 0x7f
107 }
108
109 #[inline]
110 pub fn intg_units(&self) -> usize {
111 get_units(self.intg())
112 }
113
114 #[inline]
115 pub fn frac(&self) -> i8 {
116 self.frac
117 }
118
119 #[inline]
120 pub fn frac_units(&self) -> usize {
121 get_units(self.frac())
122 }
123
124 #[inline]
125 fn reset_units(&mut self) {
126 self.lsu.iter_mut().for_each(|n| *n = 0);
127 }
128
129 #[inline]
130 pub fn as_i64(&self) -> Result<i64> {
131 if self < &MIN_I64 || self > &MAX_I64 {
132 return Err(Error::ExceedsConversionTargetRange)
133 }
134 if self.frac_units() > 0 {
135 let mut target = Self::zero();
136 self.round_to(&mut target, 0);
137 return Ok(target.lsu_to_i64())
138 }
139 Ok(self.lsu_to_i64())
140 }
141
142 fn lsu_to_i64(&self) -> i64 {
143 if self.is_neg() {
144 -self.lsu[0] as i64 - self.lsu[1] as i64 * UNIT as i64 - self.lsu[2] as i64 * UNIT as i64 * UNIT as i64
145 } else {
146 self.lsu[0] as i64 + self.lsu[1] as i64 * UNIT as i64 + self.lsu[2] as i64 * UNIT as i64 * UNIT as i64
147 }
148 }
149
150 #[inline]
151 pub fn as_u64(&self) -> Result<u64> {
152 if self.is_neg() || self > &MAX_U64 {
153 return Err(Error::ExceedsConversionTargetRange)
154 }
155 if self.frac_units() > 0 {
156 let mut target = Self::zero();
157 self.round_to(&mut target, 0);
158 return Ok(target.lsu_to_u64())
159 }
160 Ok(self.lsu_to_u64())
161 }
162
163 fn lsu_to_u64(&self) -> u64 {
164 self.lsu[0] as u64 + self.lsu[1] as u64 * UNIT as u64 + self.lsu[2] as u64 * UNIT as u64 * UNIT as u64
165 }
166}
167
168impl Hash for FixedDecimal {
173 fn hash<H: Hasher>(&self, state: &mut H) {
174 state.write_u8(if self.is_neg() { 1 } else { 0 });
176 let intg_units = self.intg_units();
178 state.write_u8(intg_units as u8);
179 let frac_units = self.frac_units();
181 state.write_u8(frac_units as u8);
182 for v in &self.lsu[..intg_units + frac_units] {
184 state.write_u32(*v as u32)
185 }
186 }
187}
188
189impl From<i64> for FixedDecimal {
190 fn from(src: i64) -> Self {
191 if src == i64::MIN {
192 return MIN_I64.clone();
193 }
194 if src < 0 {
195 let mut fd = FixedDecimal::from(-src as u64);
196 fd.set_neg();
197 fd
198 } else {
199 FixedDecimal::from(src as u64)
200 }
201 }
202}
203
204impl From<u64> for FixedDecimal {
205 fn from(mut src: u64) -> Self {
206 let mut fd = FixedDecimal::zero();
207 let mut i = 0;
208 while src != 0 {
209 let q = src / UNIT as u64;
210 let r = src - q * UNIT as u64;
211 fd.lsu[i] = r as i32;
212 i += 1;
213 src = q;
214 }
215 fd.intg = (i * DIGITS_PER_UNIT) as i8;
216 fd
217 }
218}
219
220#[cfg(test)]
221mod tests {
222
223 use std::str::FromStr;
224
225 use super::*;
226
227 #[test]
228 fn test_one() {
229 let fd = FixedDecimal::one();
230 assert!(!fd.is_neg());
231 assert!(!fd.is_zero());
232 }
233
234 #[test]
235 fn test_from_str_rand() {
236 use rand::prelude::*;
237 let mut rng = rand::thread_rng();
238 let mut fd = FixedDecimal::zero();
239 for _ in 0..128 {
240 let intg: u32 = rng.gen_range(0..1 << 30);
241 let frac: u32 = rng.gen_range(0..1 << 20);
242 let s = if frac > 0 {
243 format!("{}.{}", intg, frac)
244 } else {
245 format!("{}", intg)
246 };
247 assert!(fd.from_ascii_str(&s, true).is_ok());
248 println!("s={}, fd={:?}", s, fd);
249 }
250 }
251
252 #[test]
253 fn test_hash() {
254 for (s1, s2) in vec![("0.1", "0.10"), ("1.2", "1.2")] {
255 let fd1: FixedDecimal = s1.parse().unwrap();
256 let fd2: FixedDecimal = s2.parse().unwrap();
257 assert_eq!(decimal_hash(&fd1), decimal_hash(&fd2));
258 }
259 }
260
261 #[test]
262 fn test_from_i64() {
263 for (i, s) in vec![
264 (0i64, "0"),
265 (1, "1"),
266 (-1, "-1"),
267 (100, "100"),
268 (-100, "-100"),
269 (12345123456789, "12345123456789"),
270 (-12345123456789, "-12345123456789"),
271 (-9223372036854775808, "-9223372036854775808"),
272 (9223372036854775807, "9223372036854775807"),
273 ] {
274 let fd1 = FixedDecimal::from(i);
275 let fd2 = FixedDecimal::from_str(s).unwrap();
276 assert_eq!(fd1, fd2);
277 println!("{:?}", fd1);
278 }
279 }
280
281 #[test]
282 fn test_to_i64() {
283 for (s, i) in vec![
285 ("0", 0i64),
286 ("1", 1),
287 ("1.1", 1),
288 ("1.5", 2),
289 ("3.8", 4),
290 ("-1.2", -1),
291 ("-5.9", -6),
292 ("100", 100),
293 ("12345123456789", 12345123456789),
294 ("-9223372036854775808", -9223372036854775808),
295 ("9223372036854775807", 9223372036854775807),
296 ] {
297 let fd = FixedDecimal::from_str(s).unwrap();
298 let res = fd.as_i64().unwrap();
299 assert_eq!(res, i)
300 }
301 for s in vec![
303 "-9223372036854775809",
304 "9223372036854775808",
305 "10000000000000000000000",
306 ] {
307 let fd = FixedDecimal::from_str(s).unwrap();
308 assert!(fd.as_i64().is_err());
309 }
310 }
311
312 #[test]
313 fn test_from_u64() {
314 for (i, s) in vec![
315 (0u64, "0"),
316 (1, "1"),
317 (100, "100"),
318 (12345123456789, "12345123456789"),
319 (18446744073709551615, "18446744073709551615"),
320 ] {
321 let fd1 = FixedDecimal::from(i);
322 let fd2 = FixedDecimal::from_str(s).unwrap();
323 assert_eq!(fd1, fd2);
324 println!("{:?}", fd1);
325 }
326 }
327
328 #[test]
329 fn test_to_u64() {
330 for (s, i) in vec![
332 ("0", 0u64),
333 ("1", 1),
334 ("1.1", 1),
335 ("1.5", 2),
336 ("3.8", 4),
337 ("100", 100),
338 ("12345123456789", 12345123456789),
339 ("9223372036854775807", 9223372036854775807),
340 ("18446744073709551615", 18446744073709551615),
341 ] {
342 let fd = FixedDecimal::from_str(s).unwrap();
343 let res = fd.as_u64().unwrap();
344 assert_eq!(res, i)
345 }
346 for s in vec![
348 "-1",
349 "18446744073709551616",
350 "10000000000000000000000",
351 ] {
352 let fd = FixedDecimal::from_str(s).unwrap();
353 assert!(fd.as_u64().is_err());
354 }
355 }
356
357 #[inline]
358 fn decimal_hash(fd: &FixedDecimal) -> u64 {
359 use std::collections::hash_map::DefaultHasher;
360 let mut hasher = DefaultHasher::new();
361 fd.hash(&mut hasher);
362 hasher.finish()
363 }
364}