rust_fixed_point_decimal/
from_int.rs

1// ----------------------------------------------------------------------------
2// Copyright:   (c) 2021 ff. Michael Amrhein (michael@adrhinum.de)
3// License:     This program is part of a larger application. For license
4//              details please read the file LICENSE.TXT provided together
5//              with the application.
6// ----------------------------------------------------------------------------
7// $Source: src/from_int.rs $
8// $Revision: 2021-08-20T15:17:08+02:00 $
9
10use std::convert::TryFrom;
11
12use rust_fixed_point_decimal_core::{checked_mul_pow_ten, ten_pow};
13
14use crate::{errors::DecimalError, Decimal, PrecLimitCheck, True, MAX_PREC};
15
16macro_rules! impl_from_int {
17    () => {
18        impl_from_int!(u8, i8, u16, i16, u32, i32, u64, i64);
19    };
20    ($($t:ty),*) => {
21        $(
22        impl<const P: u8> From<$t> for Decimal<P>
23        where
24            PrecLimitCheck<{ P <= crate::MAX_PREC }>: True,
25        {
26            #[inline]
27            fn from(i: $t) -> Self
28            {
29                Decimal { coeff: i as i128 * ten_pow(P) as i128 }
30            }
31        }
32        )*
33    }
34}
35
36impl_from_int!();
37
38impl<const P: u8> TryFrom<i128> for Decimal<P>
39where
40    PrecLimitCheck<{ P <= MAX_PREC }>: True,
41{
42    type Error = DecimalError;
43
44    #[inline]
45    fn try_from(i: i128) -> Result<Self, Self::Error> {
46        match checked_mul_pow_ten(i, P) {
47            None => Err(DecimalError::MaxValueExceeded),
48            Some(coeff) => Ok(Decimal { coeff }),
49        }
50    }
51}
52
53impl<const P: u8> TryFrom<u128> for Decimal<P>
54where
55    PrecLimitCheck<{ P <= MAX_PREC }>: True,
56{
57    type Error = DecimalError;
58
59    #[inline]
60    fn try_from(i: u128) -> Result<Self, Self::Error> {
61        match i128::try_from(i) {
62            Err(_) => Err(DecimalError::MaxValueExceeded),
63            Ok(i) => Self::try_from(i),
64        }
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use std::convert::TryInto;
71
72    use super::*;
73
74    fn check_from_int<const P: u8, T>(numbers: &[T])
75    where
76        PrecLimitCheck<{ P <= MAX_PREC }>: True,
77        T: Into<i128> + Copy,
78        Decimal<P>: From<T>,
79    {
80        for n in numbers {
81            let d = Decimal::<P>::from(*n);
82            assert_eq!(d.coeff / ten_pow(P), (*n).into());
83            assert_eq!(d.precision(), P);
84        }
85    }
86
87    #[test]
88    fn test_from_u8() {
89        let numbers: [u8; 4] = [0, 1, 28, 255];
90        check_from_int::<2, u8>(&numbers);
91    }
92
93    #[test]
94    fn test_from_i8() {
95        let numbers: [i8; 7] = [-128, -38, -1, 0, 1, 28, 127];
96        check_from_int::<0, i8>(&numbers);
97    }
98
99    #[test]
100    fn test_from_u64() {
101        let numbers: [u64; 4] = [0, 1, 2128255, u64::MAX];
102        check_from_int::<9, u64>(&numbers);
103    }
104
105    #[test]
106    fn test_from_i64() {
107        let numbers: [i64; 4] = [0, -1, 2128255, i64::MIN];
108        check_from_int::<3, i64>(&numbers);
109    }
110
111    fn check_try_from_int_ok<const P: u8, T>(numbers: &[T])
112    where
113        PrecLimitCheck<{ P <= MAX_PREC }>: True,
114        T: TryInto<i128> + Copy,
115        Decimal<P>: TryFrom<T>,
116    {
117        for n in numbers {
118            match Decimal::<P>::try_from(*n) {
119                Err(_) => panic!("Mismatched test value!"),
120                Ok(d) => match (*n).try_into() {
121                    Err(_) => panic!("Should never happen!"),
122                    Ok(i) => {
123                        assert_eq!(d.coeff / ten_pow(P), i);
124                        assert_eq!(d.precision(), P);
125                    }
126                },
127            }
128        }
129    }
130
131    #[test]
132    fn test_from_u128_ok() {
133        let numbers: [u128; 4] =
134            [0, 1, 2128255, 170141183460469231731687303715884105727u128];
135        check_try_from_int_ok::<0, u128>(&numbers);
136    }
137
138    #[test]
139    fn test_from_i128_ok() {
140        let numbers: [i128; 7] = [
141            -170141183460469231731687303715884105728,
142            -3830009274,
143            -1,
144            0,
145            1,
146            2829773566410082,
147            170141183460469231731687303715884105727,
148        ];
149        check_try_from_int_ok::<0, i128>(&numbers);
150    }
151
152    #[test]
153    fn test_from_i128_err() {
154        let numbers: [i128; 2] = [
155            -170141183460469231731687303715884105728,
156            170141183460469231731687303715884105727,
157        ];
158        for i in numbers {
159            let res = Decimal::<1>::try_from(i);
160            assert_eq!(res.unwrap_err(), DecimalError::MaxValueExceeded);
161        }
162    }
163
164    #[test]
165    fn test_from_u128_err() {
166        let i = 170141183460469231731687303715884105728u128;
167        let res = Decimal::<0>::try_from(i);
168        assert_eq!(res.unwrap_err(), DecimalError::MaxValueExceeded);
169        let i = 17014118346046923173168730371588410572u128;
170        let res = Decimal::<3>::try_from(i);
171        assert_eq!(res.unwrap_err(), DecimalError::MaxValueExceeded);
172    }
173
174    #[test]
175    fn test_from() {
176        let di32 = Decimal::<3>::from(-358i32);
177        assert_eq!(di32.coeff, -358000i128);
178        let i = 38i64.pow(12);
179        let di64 = Decimal::<0>::from(i);
180        assert_eq!(di64.coeff, i as i128);
181    }
182
183    #[test]
184    fn test_into() {
185        let du8: Decimal<9> = 38u8.into();
186        assert_eq!(du8.coeff, 38000000000);
187        let i = -1234567890123456789i64;
188        let di64: Decimal<0> = i.into();
189        assert_eq!(di64.coeff, i as i128);
190    }
191}