penumbra_sdk_num/fixpoint/
from.rs

1use crate::fixpoint;
2use crate::fixpoint::U128x128;
3use ethnum::U256;
4
5impl From<u8> for U128x128 {
6    fn from(value: u8) -> Self {
7        Self(U256::from_words(value.into(), 0))
8    }
9}
10
11impl From<u16> for U128x128 {
12    fn from(value: u16) -> Self {
13        Self(U256::from_words(value.into(), 0))
14    }
15}
16
17impl From<u32> for U128x128 {
18    fn from(value: u32) -> Self {
19        Self(U256::from_words(value.into(), 0))
20    }
21}
22
23impl From<u64> for U128x128 {
24    fn from(value: u64) -> Self {
25        Self(U256::from_words(value.into(), 0))
26    }
27}
28
29impl From<u128> for U128x128 {
30    fn from(value: u128) -> Self {
31        Self(U256::from_words(value, 0))
32    }
33}
34
35impl TryFrom<U128x128> for u128 {
36    type Error = super::Error;
37    fn try_from(value: U128x128) -> Result<Self, Self::Error> {
38        match value.is_integral() {
39            true => Ok(value.0.into_words().0),
40            false => Err(super::Error::NonIntegral { value }),
41        }
42    }
43}
44
45impl TryFrom<U128x128> for u64 {
46    type Error = super::Error;
47    fn try_from(value: U128x128) -> Result<Self, Self::Error> {
48        u128::try_from(value).and_then(|x| u64::try_from(x).map_err(|_| super::Error::Overflow))
49    }
50}
51
52impl From<[u8; 32]> for U128x128 {
53    fn from(value: [u8; 32]) -> Self {
54        Self::from_bytes(value)
55    }
56}
57
58impl From<U128x128> for [u8; 32] {
59    fn from(value: U128x128) -> Self {
60        value.to_bytes()
61    }
62}
63
64impl From<U128x128> for f64 {
65    fn from(value: U128x128) -> Self {
66        // This is a hack but it seems to work mostly?
67        // doesn't seem critical for it to be exact, there's no reverse conversion.
68        let (hi, lo) = value.0.into_words();
69        // binary repr of 2^128
70        const BASE: u64 = 0x47f0000000000000;
71        (hi as f64) + (lo as f64) / f64::from_bits(BASE)
72    }
73}
74
75impl TryFrom<f64> for U128x128 {
76    type Error = fixpoint::Error;
77
78    #[allow(clippy::if_same_then_else)]
79    fn try_from(value: f64) -> Result<U128x128, Self::Error> {
80        if value < 0.0 {
81            Err(fixpoint::Error::InvalidFloat64 { value })
82        } else if value.is_infinite() {
83            Err(fixpoint::Error::InvalidFloat64 { value })
84        } else if value.is_nan() {
85            Err(fixpoint::Error::InvalidFloat64 { value })
86        } else {
87            let integral = value as u128;
88            let fractional = value.fract();
89
90            // Convert the fractional part to an unsigned 128-bit integer.
91            // 1. Multiply the fractional part by 2^128 to move the significant bits to the left.
92            // 2. Round down the result to the nearest integer.
93            // 3. Convert the rounded result to an unsigned 128-bit integer.
94            let fractional_as_u128 =
95                (fractional * f64::from_bits(0x47f0000000000000)).trunc() as u128;
96            let combined = U256::from_words(integral, fractional_as_u128);
97            Ok(U128x128(combined))
98        }
99    }
100}
101
102impl From<U128x128> for Vec<u8> {
103    fn from(value: U128x128) -> Self {
104        value.to_bytes().to_vec()
105    }
106}
107
108impl TryFrom<&[u8]> for U128x128 {
109    type Error = super::Error;
110    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
111        Ok(<[u8; 32]>::try_from(value)
112            .map_err(|_| super::Error::SliceLength(value.len()))?
113            .into())
114    }
115}