Skip to main content

nanvm_lib/vm/bigint/
to_f64.rs

1use super::BigInt;
2use crate::{
3    common::sized_index::SizedIndex,
4    vm::{IVm, Number},
5};
6
7impl<A: IVm> BigInt<A> {
8    /// `Number(bigint)`: the nearest `Number`, a tie going to the even
9    /// significand, and `Infinity` or `-Infinity` past the range.
10    /// <https://tc39.es/ecma262/#sec-numberbigint>
11    ///
12    /// The top two words are the significand [`Number::compose`] rounds, and
13    /// any word below them set is its sticky bit: with the top word not
14    /// zero, the two hold at least 65 significant bits, more than the 54 the
15    /// sticky bit needs.
16    pub fn to_number(&self) -> Number {
17        let length = self.length();
18        let (significand, words_below, sticky) = match length {
19            0 => (0, 0, false),
20            1 => (u128::from(self[0]), 0, false),
21            _ => (
22                (u128::from(self[length - 1]) << 64) | u128::from(self[length - 2]),
23                length - 2,
24                (0..length - 2).any(|i| self[i] != 0),
25            ),
26        };
27        let exponent =
28            i32::try_from(64 * words_below).expect("a BigInt's words are far under 2^31 bits");
29        Number::compose(self.sign(), significand, exponent, sticky)
30            .expect("`sticky` is set only under a nonzero top word: over 64 significant bits")
31    }
32}
33
34#[cfg(test)]
35mod tests {
36    use crate::{
37        naive::Naive,
38        sign::Sign,
39        vm::{BigInt, Number},
40    };
41
42    fn n(sign: Sign, words: &[u64]) -> f64 {
43        f64::from(BigInt::<Naive>::normalize_new(sign, words.iter().copied()).to_number())
44    }
45
46    fn pos(words: &[u64]) -> f64 {
47        n(Sign::Positive, words)
48    }
49
50    #[test]
51    fn small() {
52        assert_eq!(pos(&[]), 0.0);
53        assert!(pos(&[]).is_sign_positive());
54        assert!(n(Sign::Negative, &[]).is_sign_positive());
55        assert_eq!(pos(&[1]), 1.0);
56        assert_eq!(n(Sign::Negative, &[1]), -1.0);
57        assert_eq!(pos(&[u64::MAX]), u64::MAX as f64);
58    }
59
60    #[test]
61    fn two_to_the_53() {
62        assert_eq!(pos(&[1 << 53]), 9_007_199_254_740_992.0);
63        // 2^53 + 1 is a tie between 2^53 and 2^53 + 2: the even one.
64        assert_eq!(pos(&[(1 << 53) + 1]), 9_007_199_254_740_992.0);
65        // 2^53 + 3 is a tie too, and the even significand is the upper one.
66        assert_eq!(pos(&[(1 << 53) + 3]), 9_007_199_254_740_996.0);
67    }
68
69    #[test]
70    fn sticky_bits_break_a_tie() {
71        // 2^64 + 2^11 is half way between 2^64 and 2^64 + 2^12: a tie, even
72        // is 2^64. One more bit anywhere below goes up.
73        let two_64 = 18_446_744_073_709_551_616.0;
74        assert_eq!(pos(&[1 << 11, 1]), two_64);
75        assert_eq!(pos(&[(1 << 11) + 1, 1]), two_64 + 4_096.0);
76        // The same with the extra bit two words down.
77        assert_eq!(pos(&[1, 1 << 11, 1]), two_64 * two_64 + two_64 * 4_096.0);
78        assert_eq!(pos(&[0, 1 << 11, 1]), two_64 * two_64);
79    }
80
81    #[test]
82    fn several_words() {
83        // 123456789012345678901234567890n
84        assert_eq!(
85            pos(&[0xC373_E0EE_4E3F_0AD2, 0x1_8EE9_0FF6]),
86            123_456_789_012_345_678_901_234_567_890.0
87        );
88        assert_eq!(pos(&[0, 1]), 18_446_744_073_709_551_616.0);
89        assert_eq!(
90            pos(&[0, 0, 1]),
91            340_282_366_920_938_463_463_374_607_431_768_211_456.0
92        );
93    }
94
95    #[test]
96    fn range() {
97        // 2^1023 words: bit 1023 is bit 63 of word 15.
98        let mut words = vec![0u64; 16];
99        words[15] = 1 << 63;
100        assert_eq!(pos(&words), 2f64.powi(1_023));
101        // Just under 2^1024 rounds up to it: Infinity.
102        let all = vec![u64::MAX; 16];
103        assert_eq!(pos(&all), f64::INFINITY);
104        assert_eq!(n(Sign::Negative, &all), f64::NEG_INFINITY);
105        // The largest finite value: 53 ones, then zeros.
106        let mut words = vec![0u64; 16];
107        words[15] = u64::MAX << 11;
108        assert_eq!(pos(&words), f64::MAX);
109        // Past the range by words.
110        assert_eq!(pos(&vec![1u64; 40]), f64::INFINITY);
111    }
112
113    #[test]
114    fn never_nan() {
115        let v: Number = BigInt::<Naive>::normalize_new(Sign::Positive, [0u64; 3]).to_number();
116        assert_eq!(f64::from(v), 0.0);
117    }
118}