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}