clickhouse_rs_async/types/
decimal.rs

1use std::{
2    cmp::Ordering,
3    fmt,
4    hash::{Hash, Hasher},
5};
6
7static FACTORS10: &[i64] = &[
8    1,
9    10,
10    100,
11    1000,
12    10000,
13    100_000,
14    1_000_000,
15    10_000_000,
16    100_000_000,
17    1_000_000_000,
18    10_000_000_000,
19    100_000_000_000,
20    1_000_000_000_000,
21    10_000_000_000_000,
22    100_000_000_000_000,
23    1_000_000_000_000_000,
24    10_000_000_000_000_000,
25    100_000_000_000_000_000,
26    1_000_000_000_000_000_000,
27];
28
29pub trait Base {
30    fn scale(self, scale: i64) -> i64;
31}
32
33pub trait InternalResult {
34    fn get(underlying: i64) -> Self;
35}
36
37#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
38pub(crate) enum NoBits {
39    N32,
40    N64,
41}
42
43/// Provides arbitrary-precision floating point decimal.
44#[derive(Clone)]
45pub struct Decimal {
46    pub(crate) underlying: i64,
47    pub(crate) nobits: NoBits, // its domain is {32, 64}
48    pub(crate) precision: u8,
49    pub(crate) scale: u8,
50}
51
52impl Default for Decimal {
53    fn default() -> Self {
54        Decimal {
55            underlying: 0,
56            precision: 9,
57            scale: 4,
58            nobits: NoBits::N32,
59        }
60    }
61}
62
63impl Hash for Decimal {
64    fn hash<H: Hasher>(&self, state: &mut H) {
65        self.underlying.hash(state);
66        self.nobits.hash(state);
67        self.precision.hash(state);
68        self.scale.hash(state);
69    }
70}
71
72macro_rules! base_for {
73    ( $( $t:ty: $cast:expr ),* ) => {
74        $(
75            impl Base for $t {
76                fn scale(self, scale: i64) -> i64 {
77                    $cast(self * (scale as $t)) as i64
78                }
79            }
80        )*
81    };
82}
83
84base_for! {
85    f32: std::convert::identity,
86    f64: std::convert::identity,
87    i8: i64::from,
88    i16: i64::from,
89    i32: i64::from,
90    i64: std::convert::identity,
91    u8: i64::from,
92    u16: i64::from,
93    u32: i64::from,
94    u64 : std::convert::identity
95}
96
97impl InternalResult for i32 {
98    #[inline(always)]
99    fn get(underlying: i64) -> Self {
100        underlying as Self
101    }
102}
103
104impl InternalResult for i64 {
105    #[inline(always)]
106    fn get(underlying: i64) -> Self {
107        underlying
108    }
109}
110
111impl NoBits {
112    pub(crate) fn from_precision(precision: u8) -> Option<NoBits> {
113        if precision <= 9 {
114            Some(NoBits::N32)
115        } else if precision <= 18 {
116            Some(NoBits::N64)
117        } else {
118            None
119        }
120    }
121}
122
123impl PartialEq for Decimal {
124    fn eq(&self, other: &Self) -> bool {
125        match self.scale.cmp(&other.scale) {
126            Ordering::Less => {
127                let delta = other.scale() - self.scale();
128                let underlying = self.underlying * FACTORS10[delta];
129                other.underlying == underlying
130            }
131            Ordering::Equal => self.underlying == other.underlying,
132            Ordering::Greater => {
133                let delta = self.scale() - other.scale();
134                let underlying = other.underlying * FACTORS10[delta];
135                self.underlying == underlying
136            }
137        }
138    }
139}
140
141fn decimal2str(decimal: &Decimal) -> String {
142    let mut r = format!("{}", decimal.underlying);
143    while r.len() < decimal.scale() {
144        r.insert(0, '0');
145    }
146    let pos = r.len() - decimal.scale();
147    r.insert(pos, '.');
148    if r.starts_with('.') {
149        r.insert(0, '0');
150    }
151    r
152}
153
154impl fmt::Display for Decimal {
155    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
156        write!(f, "{}", decimal2str(self))
157    }
158}
159
160impl fmt::Debug for Decimal {
161    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
162        write!(f, "{}", decimal2str(self))
163    }
164}
165
166impl From<Decimal> for f32 {
167    fn from(value: Decimal) -> Self {
168        value.underlying as f32 / FACTORS10[value.scale()] as f32
169    }
170}
171
172impl From<Decimal> for f64 {
173    fn from(value: Decimal) -> Self {
174        value.underlying as f64 / FACTORS10[value.scale()] as f64
175    }
176}
177
178impl Decimal {
179    /// Method of creating a Decimal.
180    pub fn new(underlying: i64, scale: u8) -> Decimal {
181        let precision = 18;
182        if scale > precision {
183            panic!("scale can't be greater than 18");
184        }
185
186        Decimal {
187            underlying,
188            precision,
189            scale,
190            nobits: NoBits::N64,
191        }
192    }
193
194    pub fn of<B: Base>(source: B, scale: u8) -> Decimal {
195        let precision = 18;
196        if scale > precision {
197            panic!("scale can't be greater than 18");
198        }
199
200        let underlying = source.scale(FACTORS10[scale as usize]);
201        if underlying > FACTORS10[precision as usize] {
202            panic!("{} > {}", underlying, FACTORS10[precision as usize]);
203        }
204
205        Decimal {
206            underlying,
207            precision,
208            scale,
209            nobits: NoBits::N64,
210        }
211    }
212
213    /// Get the internal representation of decimal as [`i32`] or [`i64`].
214    ///
215    /// example:
216    /// ```rust
217    /// # use std::env;
218    /// # use clickhouse_rs::{Pool, types::Decimal, errors::Result};
219    /// # let mut rt = tokio::runtime::Runtime::new().unwrap();
220    /// # let ret: Result<()> = rt.block_on(async {
221    /// #     let database_url = env::var("DATABASE_URL")
222    /// #         .unwrap_or("tcp://localhost:9000?compression=lz4".into());
223    /// #     let pool = Pool::new(database_url);
224    ///       let mut c = pool.get_handle().await?;
225    ///       let block = c.query("SELECT toDecimal32(2, 4) AS x").fetch_all().await?;
226    ///
227    ///       let x: Decimal = block.get(0, "x")?;
228    ///       let actual: i32 = x.internal();
229    ///       assert_eq!(20000, actual);
230    /// #     Ok(())
231    /// # });
232    /// # ret.unwrap()
233    /// ```
234    #[inline(always)]
235    pub fn internal<I: InternalResult>(&self) -> I {
236        InternalResult::get(self.underlying)
237    }
238
239    /// Determines how many decimal digits fraction can have.
240    pub fn scale(&self) -> usize {
241        self.scale as usize
242    }
243
244    pub(crate) fn set_scale(self, scale: u8) -> Self {
245        let underlying = match scale.cmp(&self.scale) {
246            Ordering::Less => {
247                let delta = self.scale() - scale as usize;
248                self.underlying / FACTORS10[delta]
249            }
250            Ordering::Equal => return self,
251            Ordering::Greater => {
252                let delta = scale as usize - self.scale();
253                self.underlying * FACTORS10[delta]
254            }
255        };
256
257        Decimal {
258            underlying,
259            precision: self.precision,
260            scale,
261            nobits: self.nobits,
262        }
263    }
264}
265
266#[cfg(test)]
267mod test {
268    use super::*;
269
270    #[test]
271    fn test_new() {
272        assert_eq!(Decimal::new(2, 1), Decimal::of(0.2_f64, 1));
273        assert_eq!(Decimal::new(2, 5), Decimal::of(0.00002_f64, 5));
274    }
275
276    #[test]
277    fn test_display() {
278        assert_eq!(format!("{}", Decimal::of(2.1_f32, 4)), "2.1000");
279        assert_eq!(format!("{}", Decimal::of(0.2_f64, 4)), "0.2000");
280        assert_eq!(format!("{}", Decimal::of(2, 4)), "2.0000");
281        assert_eq!(format!("{:?}", Decimal::of(2, 4)), "2.0000");
282    }
283
284    #[test]
285    fn test_eq() {
286        assert_eq!(Decimal::of(2.0_f64, 4), Decimal::of(2.0_f64, 4));
287        assert_ne!(Decimal::of(3.0_f64, 4), Decimal::of(2.0_f64, 4));
288
289        assert_eq!(Decimal::of(2.0_f64, 4), Decimal::of(2.0_f64, 2));
290        assert_ne!(Decimal::of(2.0_f64, 4), Decimal::of(3.0_f64, 2));
291
292        assert_eq!(Decimal::of(2.0_f64, 2), Decimal::of(2.0_f64, 4));
293        assert_ne!(Decimal::of(3.0_f64, 2), Decimal::of(2.0_f64, 4));
294    }
295
296    #[test]
297    fn test_internal32() {
298        let internal: i32 = Decimal::of(2, 4).internal();
299        assert_eq!(internal, 20000_i32);
300    }
301
302    #[test]
303    fn test_internal64() {
304        let internal: i64 = Decimal::of(2, 4).internal();
305        assert_eq!(internal, 20000_i64);
306    }
307
308    #[test]
309    fn test_scale() {
310        assert_eq!(Decimal::of(2, 4).scale(), 4);
311    }
312
313    #[test]
314    fn test_from_f32() {
315        let value: f32 = Decimal::of(2, 4).into();
316        assert!((value - 2.0_f32).abs() <= std::f32::EPSILON);
317    }
318
319    #[test]
320    fn test_from_f64() {
321        let value: f64 = Decimal::of(2, 4).into();
322        assert!((value - 2.0_f64).abs() < std::f64::EPSILON);
323    }
324
325    #[test]
326    fn set_scale1() {
327        let a = Decimal::of(12, 3);
328        let b = a.set_scale(2);
329
330        assert_eq!(2, b.scale);
331        assert_eq!(1200, b.underlying);
332    }
333
334    #[test]
335    fn set_scale2() {
336        let a = Decimal::of(12, 3);
337        let b = a.set_scale(4);
338
339        assert_eq!(4, b.scale);
340        assert_eq!(120_000, b.underlying);
341    }
342
343    #[test]
344    fn test_decimal2str() {
345        let d = Decimal::of(0.00001, 5);
346        let actual = decimal2str(&d);
347        assert_eq!(actual, "0.00001".to_string());
348    }
349}