1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use crate::{Decimal, Int128_18, Int64_9, ScaledInteger, Uint128_18, Uint64_9};

// TODO: Implement From generically where the result cannot overflow.
// TODO: Implement TryFrom generically where the result can overflow.

impl From<Uint64_9> for Uint128_18 {
    fn from(value: Uint64_9) -> Self {
        // We know this multiplication can never overflow.
        #[allow(clippy::arithmetic_side_effects)]
        Decimal((u128::from(value.0)) * 10u128.pow(9))
    }
}

impl From<Int64_9> for Int128_18 {
    fn from(value: Int64_9) -> Self {
        // We know this multiplication can never overflow.
        #[allow(clippy::arithmetic_side_effects)]
        Decimal((i128::from(value.0)) * 10i128.pow(9))
    }
}

impl<I, const D: u8> Decimal<I, D>
where
    I: ScaledInteger<D>,
{
    // SAFETY: `num_traits::to_f64` does not panic on primitive types.
    #[allow(clippy::missing_panics_doc)]
    pub fn to_f64(&self) -> f64 {
        self.0.to_f64().unwrap() / I::SCALING_FACTOR.to_f64().unwrap()
    }
}

#[cfg(test)]
mod tests {
    use std::str::FromStr;

    use proptest::prelude::Arbitrary;
    use proptest::proptest;
    use proptest::test_runner::TestRunner;

    use super::*;
    use crate::macros::generate_tests_for_common_variants;

    #[test]
    fn uint128_18_from_uint64_9() {
        let mut runner = TestRunner::default();
        let input = Decimal::arbitrary();

        runner
            .run(&input, |decimal: Decimal<u64, 9>| {
                let out = Uint128_18::from(decimal);
                let out_f = f64::from_str(&out.to_string()).unwrap();
                let decimal_f = f64::from_str(&decimal.to_string()).unwrap();

                assert_eq!(out_f, decimal_f);

                Ok(())
            })
            .unwrap();
    }

    #[test]
    fn int128_18_from_int64_9() {
        let mut runner = TestRunner::default();
        let input = Decimal::arbitrary();

        runner
            .run(&input, |decimal: Decimal<i64, 9>| {
                let out = Int128_18::from(decimal);
                let out_f = f64::from_str(&out.to_string()).unwrap();
                let decimal_f = f64::from_str(&decimal.to_string()).unwrap();

                assert_eq!(out_f, decimal_f);

                Ok(())
            })
            .unwrap();
    }

    generate_tests_for_common_variants!(to_f64_does_not_panic);

    fn to_f64_does_not_panic<I, const D: u8>()
    where
        I: ScaledInteger<D> + Arbitrary,
    {
        proptest!(|(a: Decimal<I, D>)| {
            a.to_f64();
        });
    }
}