Skip to main content

nanvm_lib/vm/number/
mod.rs

1mod add;
2mod binary64;
3mod debug;
4mod div;
5mod format;
6mod from;
7mod int32_coercion;
8mod mul;
9mod neg;
10mod pow;
11mod rem;
12mod sub;
13mod to_integer_or_infinity;
14
15/// A JavaScript number: an `f64` that holds one `NaN`.
16///
17/// FunctionalScript has one `NaN`: nothing in the language tells two apart,
18/// so a sign or payload a host operation left on one — `-NaN` flips the
19/// sign, x86's `0 / 0` sets it — is dropped where an `f64` becomes a
20/// `Number`, and every operator's result passes back through the same
21/// conversion. The field is private, so there is no other way to build one,
22/// and a NaN-boxing VM, which keeps its boxed values in the negative quiet
23/// `NaN`s, can store the bits as they are.
24///
25/// This is the VM's number: a literal or a parsed string becomes one once,
26/// and the VM computes on it from there — the ECMAScript operations on a
27/// `Number` are its methods and operators. An `f64` is read back out only
28/// where a leaf needs IEEE 754 arithmetic Rust already has, and at a
29/// test's edge to look at the bits.
30///
31/// `PartialOrd` is IEEE 754's: a `NaN` compares with nothing, which is
32/// exactly the `undefined` a relational operator's abstract comparison
33/// answers.
34#[repr(transparent)]
35#[derive(Clone, Copy, PartialEq, PartialOrd)]
36pub struct Number(f64);
37
38/// The names are JavaScript's, in Rust's casing: `Number.NaN` is `NAN`,
39/// `Number.isFinite` is `is_finite`, and each does what its original does.
40impl Number {
41    /// `Number.NaN` — the one `NaN`: quiet, positive, empty payload.
42    pub const NAN: Self = Self(f64::from_bits(0x7ff8_0000_0000_0000));
43
44    /// `Number.isNaN(self)`.
45    pub fn is_nan(self) -> bool {
46        self.0.is_nan()
47    }
48
49    /// `Number.isFinite(self)`: neither a `NaN` nor an infinity.
50    pub fn is_finite(self) -> bool {
51        self.0.is_finite()
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::Number;
58    use crate::{
59        naive::Naive,
60        vm::{Any, ToAny, Unpacked, numeric::Numeric, primitive::Primitive},
61    };
62
63    pub(super) fn bits(v: Number) -> u64 {
64        f64::from(v).to_bits()
65    }
66
67    /// Every `NaN` a host can hand over — the sign flipped, a payload set,
68    /// a signaling one — is the canonical `NaN` once it is a `Number`.
69    #[test]
70    fn every_nan_is_the_canonical_one() {
71        for v in [
72            f64::NAN,
73            -f64::NAN,
74            f64::from_bits(0x7ff8_0000_0000_0001),
75            f64::from_bits(0xfff8_0000_0000_0001),
76            f64::from_bits(0x7ff0_0000_0000_0001),
77        ] {
78            assert!(v.is_nan());
79            assert_eq!(bits(v.into()), bits(Number::NAN));
80        }
81    }
82
83    /// Only a `NaN` is touched: `-0` keeps its sign, and every other bit
84    /// pattern is passed through as it came, and comes back the same.
85    #[test]
86    fn other_numbers_keep_their_bits() {
87        for v in [
88            0.0,
89            -0.0,
90            1.5,
91            f64::INFINITY,
92            f64::NEG_INFINITY,
93            f64::MIN_POSITIVE,
94            f64::from_bits(1),
95        ] {
96            assert_eq!(bits(v.into()), v.to_bits());
97        }
98    }
99
100    /// `ToNumber` of a boolean is exact: `true` is `1`, `false` is `0`.
101    #[test]
102    fn from_bool() {
103        assert_eq!(bits(true.into()), 1.0f64.to_bits());
104        assert_eq!(bits(false.into()), 0.0f64.to_bits());
105    }
106
107    /// The paths a number takes into a VM value all go through `Number`: a
108    /// bare `f64`, a `Numeric`, a `Primitive`, and an operator on `Any`.
109    #[test]
110    fn every_way_in_canonicalizes() {
111        let unpacked_bits = |v: Unpacked<Naive>| match v {
112            Unpacked::Number(n) => bits(n),
113            _ => panic!("a number"),
114        };
115        let nan = -f64::NAN;
116        assert_ne!(nan.to_bits(), bits(Number::NAN));
117        assert_eq!(
118            unpacked_bits(nan.to_any::<Naive>().into()),
119            bits(Number::NAN)
120        );
121        assert_eq!(
122            unpacked_bits(Numeric::<Naive>::Number(nan.into()).into()),
123            bits(Number::NAN)
124        );
125        assert_eq!(
126            unpacked_bits(Primitive::<Naive>::Number(nan.into()).into()),
127            bits(Number::NAN)
128        );
129        let negated: Any<Naive> = (-f64::NAN.to_any::<Naive>()).unwrap();
130        assert_eq!(unpacked_bits(negated.into()), bits(Number::NAN));
131    }
132}