rust_fixed_point_decimal/
from_float.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_float.rs $
8// $Revision: 2021-11-01T15:06:20+01:00 $
9
10use std::convert::TryFrom;
11
12use num::{traits::float::FloatCore, BigInt, One};
13use rust_fixed_point_decimal_core::ten_pow;
14
15use crate::{
16    errors::DecimalError,
17    prec_constraints::{PrecLimitCheck, True},
18    rounding::div_i128_rounded,
19    Decimal,
20};
21
22macro_rules! impl_from_float {
23    () => {
24        impl_from_float!(f32, f64);
25    };
26    ($($t:ty),*) => {
27        $(
28        impl<const P: u8> TryFrom<$t> for Decimal<P>
29        where
30            PrecLimitCheck<{ P <= crate::MAX_PREC }>: True,
31        {
32            type Error = DecimalError;
33
34            fn try_from(f: $t) -> Result<Self, Self::Error> {
35                if f.is_infinite() {
36                    return Err(DecimalError::InfiniteValue);
37                }
38                if f.is_nan() {
39                    return Err(DecimalError::NotANumber);
40                }
41                let (mantissa, exponent, sign) = f.integer_decode();
42                if exponent < -126 {
43                    Ok(Decimal::ZERO)
44                }
45                else if exponent < 0 {
46                    let numer = i128::from(sign)
47                        * i128::from(mantissa)
48                        * ten_pow(P);
49                    let denom = i128::one() << ((-exponent) as usize);
50                    let coeff = div_i128_rounded(numer, denom, None);
51                    Ok(Decimal { coeff })
52                } else {
53                    let mut numer = BigInt::from(mantissa);
54                    numer <<= exponent as usize;
55                    numer *= BigInt::from(sign) * BigInt::from(ten_pow(P));
56                    match i128::try_from(numer) {
57                        Err(_) => Err(DecimalError::MaxValueExceeded),
58                        Ok(coeff) => Ok(Decimal { coeff }),
59                    }
60                }
61            }
62        }
63        )*
64    }
65}
66
67impl_from_float!();
68
69#[cfg(test)]
70mod tests {
71    use num::traits::float::FloatCore;
72
73    use super::*;
74    use crate::MAX_PREC;
75
76    fn check_from_float<const P: u8, T>(test_data: &[(T, i128)])
77    where
78        PrecLimitCheck<{ P <= MAX_PREC }>: True,
79        T: FloatCore,
80        Decimal<P>: TryFrom<T>,
81    {
82        for (val, coeff) in test_data {
83            match Decimal::<P>::try_from(*val) {
84                Err(_) => panic!("Mismatched test data!"),
85                Ok(d) => {
86                    assert_eq!(d.coeff, *coeff);
87                    assert_eq!(d.precision(), P);
88                }
89            }
90        }
91    }
92
93    #[test]
94    fn test_decimal0_from_f32() {
95        let test_data = [
96            (i128::MIN as f32, i128::MIN),
97            (-289.04, -289),
98            (-2.5, -2),
99            (0.0, 0),
100            (5.2, 5),
101            ((i128::MAX / 2) as f32, i128::MAX / 2 + 1),
102        ];
103        check_from_float::<0, f32>(&test_data)
104    }
105
106    #[test]
107    fn test_decimal4_from_f32() {
108        let test_data = [
109            (-289.5, -2895000),
110            (-0.5, -5000),
111            (0.0, 0),
112            (37.0005003, 370005),
113        ];
114        check_from_float::<4, f32>(&test_data)
115    }
116
117    #[test]
118    fn test_decimal0_from_f64() {
119        let test_data = [
120            (i128::MIN as f64, i128::MIN),
121            (-289.4, -289),
122            (-2.5, -2),
123            (0.0, 0),
124            (5.2, 5),
125            ((i128::MAX / 2) as f64, i128::MAX / 2 + 1),
126        ];
127        check_from_float::<0, f64>(&test_data)
128    }
129
130    #[test]
131    fn test_decimal9_from_f64() {
132        let test_data = [
133            (-28900.000000005, -28900000000005),
134            (-5e-7, -500),
135            (1.004e-127, 0),
136            (0.0, 0),
137            (1.0005, 1000500000),
138            (37.0005000033, 37000500003),
139        ];
140        check_from_float::<9, f64>(&test_data)
141    }
142
143    #[test]
144    fn test_fail_on_f32_infinite_value() {
145        for f in [f32::infinity(), f32::neg_infinity()] {
146            let res = Decimal::<2>::try_from(f);
147            assert!(res.is_err());
148            let err = res.unwrap_err();
149            assert_eq!(err, DecimalError::InfiniteValue);
150        }
151    }
152
153    #[test]
154    fn test_fail_on_f64_infinite_value() {
155        for f in [f64::infinity(), f64::neg_infinity()] {
156            let res = Decimal::<2>::try_from(f);
157            assert!(res.is_err());
158            let err = res.unwrap_err();
159            assert_eq!(err, DecimalError::InfiniteValue);
160        }
161    }
162
163    #[test]
164    fn test_fail_on_f32_nan() {
165        let f = f32::nan();
166        let res = Decimal::<2>::try_from(f);
167        assert!(res.is_err());
168        let err = res.unwrap_err();
169        assert_eq!(err, DecimalError::NotANumber);
170    }
171
172    #[test]
173    fn test_fail_on_f64_nan() {
174        let f = f64::nan();
175        let res = Decimal::<7>::try_from(f);
176        assert!(res.is_err());
177        let err = res.unwrap_err();
178        assert_eq!(err, DecimalError::NotANumber);
179    }
180}