Skip to main content

nanvm_lib/vm/bigint/
from.rs

1use super::BigInt;
2use crate::{
3    common::default::default,
4    sign::Sign,
5    vm::{IVm, Number},
6};
7
8impl<A: IVm> From<u64> for BigInt<A> {
9    fn from(value: u64) -> Self {
10        if value == 0 {
11            return default();
12        }
13        Self::new_one(Sign::Positive, value)
14    }
15}
16
17fn neg(i: i64) -> u64 {
18    i.overflowing_neg().0 as u64
19}
20
21impl<A: IVm> From<i64> for BigInt<A> {
22    fn from(value: i64) -> Self {
23        if value >= 0 {
24            return (value as u64).into();
25        }
26        Self::new_one(Sign::Negative, neg(value))
27    }
28}
29
30impl<A: IVm> BigInt<A> {
31    /// The exact `BigInt` of an integral, finite `n`, read from its binary64
32    /// layout rather than any decimal round-trip: past `2^53` a `Number` is
33    /// still an exact integer, one with known trailing zero bits. `-0` is
34    /// `0n`. A fraction, `NaN` or an infinity has no exact `BigInt` and is
35    /// refused with `None`, in every build: the type can't carry the
36    /// precondition, so the check is here rather than left to the caller.
37    pub(crate) fn from_integral(n: Number) -> Option<Self> {
38        if f64::from(n).fract() != 0.0 {
39            return None;
40        }
41        let (sign, significand, exponent) = n.decompose()?;
42        let magnitude = if exponent >= 0 {
43            (Self::from(significand) << Self::from(exponent as u64))
44                .expect("a binary64's exponent is far under BigInt's size limit")
45        } else {
46            // The bits under the point are zeros, so dropping them is exact.
47            // The shift reaches 64 or past it only for a zero.
48            Self::from(
49                significand
50                    .checked_shr(exponent.unsigned_abs())
51                    .unwrap_or(0),
52            )
53        };
54        Some(if sign == Sign::Negative {
55            -magnitude
56        } else {
57            magnitude
58        })
59    }
60
61    /// `2^e`, for an `e` a binary64's exact arithmetic reaches: a few
62    /// thousand at most, far under `BigInt`'s size limit.
63    pub(crate) fn pow2(e: u32) -> Self {
64        (Self::from(1u64) << Self::from(u64::from(e)))
65            .expect("a binary64's exponent is far under BigInt's size limit")
66    }
67}
68
69#[cfg(test)]
70mod test {
71    use crate::{
72        naive::Naive,
73        sign::Sign,
74        vm::{BigInt, Number, bigint::from::neg},
75    };
76
77    fn integral(v: f64) -> BigInt<Naive> {
78        BigInt::from_integral(Number::from(v)).expect("an integral, finite Number")
79    }
80
81    #[test]
82    fn from_integral() {
83        assert_eq!(integral(0.0), BigInt::default());
84        assert_eq!(integral(-0.0), BigInt::default());
85        assert_eq!(integral(1.0), BigInt::from(1u64));
86        assert_eq!(integral(-3.0), BigInt::from(-3i64));
87        // 2^53 and its even neighbor, past the integers every `Number` has.
88        assert_eq!(integral(9_007_199_254_740_992.0), BigInt::from(1u64 << 53));
89        assert_eq!(
90            integral(9_007_199_254_740_994.0),
91            BigInt::from((1u64 << 53) + 2)
92        );
93        assert_eq!(
94            integral(-18_446_744_073_709_551_616.0),
95            BigInt::normalize_new(Sign::Negative, [0, 1])
96        );
97        // The largest finite value: 53 ones, then 971 zeros.
98        assert_eq!(
99            integral(f64::MAX),
100            BigInt::from((1u64 << 53) - 1).mul_bounded(BigInt::pow2(971))
101        );
102    }
103
104    #[test]
105    fn from_integral_refuses() {
106        for v in [0.5, -1.5, f64::NAN, f64::INFINITY, f64::NEG_INFINITY] {
107            assert_eq!(BigInt::<Naive>::from_integral(Number::from(v)), None);
108        }
109    }
110
111    #[test]
112    fn pow2() {
113        assert_eq!(BigInt::<Naive>::pow2(0), BigInt::from(1u64));
114        assert_eq!(BigInt::<Naive>::pow2(63), BigInt::from(1u64 << 63));
115        assert_eq!(
116            BigInt::<Naive>::pow2(64),
117            BigInt::normalize_new(Sign::Positive, [0, 1])
118        );
119    }
120
121    #[test]
122    fn overflow_min1() {
123        let x = neg(i64::MIN + 1);
124        assert_eq!(x, (1 << 63) - 1);
125    }
126    #[test]
127    fn overflow_min() {
128        let x = neg(i64::MIN);
129        assert_eq!(x, 1 << 63);
130    }
131}