boa/builtins/number/
conversions.rs

1/// Converts a 64-bit floating point number to an `i32` according to the [`ToInt32`][ToInt32] algorithm.
2///
3/// [ToInt32]: https://tc39.es/ecma262/#sec-toint32
4#[inline]
5#[allow(clippy::float_cmp)]
6pub(crate) fn f64_to_int32(number: f64) -> i32 {
7    const SIGN_MASK: u64 = 0x8000000000000000;
8    const EXPONENT_MASK: u64 = 0x7FF0000000000000;
9    const SIGNIFICAND_MASK: u64 = 0x000FFFFFFFFFFFFF;
10    const HIDDEN_BIT: u64 = 0x0010000000000000;
11    const PHYSICAL_SIGNIFICAND_SIZE: i32 = 52; // Excludes the hidden bit.
12    const SIGNIFICAND_SIZE: i32 = 53;
13
14    const EXPONENT_BIAS: i32 = 0x3FF + PHYSICAL_SIGNIFICAND_SIZE;
15    const DENORMAL_EXPONENT: i32 = -EXPONENT_BIAS + 1;
16
17    #[inline]
18    fn is_denormal(number: f64) -> bool {
19        (number.to_bits() & EXPONENT_MASK) == 0
20    }
21
22    #[inline]
23    fn exponent(number: f64) -> i32 {
24        if is_denormal(number) {
25            return DENORMAL_EXPONENT;
26        }
27
28        let d64 = number.to_bits();
29        let biased_e = ((d64 & EXPONENT_MASK) >> PHYSICAL_SIGNIFICAND_SIZE) as i32;
30
31        biased_e - EXPONENT_BIAS
32    }
33
34    #[inline]
35    fn significand(number: f64) -> u64 {
36        let d64 = number.to_bits();
37        let significand = d64 & SIGNIFICAND_MASK;
38
39        if !is_denormal(number) {
40            significand + HIDDEN_BIT
41        } else {
42            significand
43        }
44    }
45
46    #[inline]
47    fn sign(number: f64) -> i64 {
48        if (number.to_bits() & SIGN_MASK) == 0 {
49            1
50        } else {
51            -1
52        }
53    }
54
55    if number.is_finite() && number <= f64::from(i32::MAX) && number >= f64::from(i32::MIN) {
56        let i = number as i32;
57        if f64::from(i) == number {
58            return i;
59        }
60    }
61
62    let exponent = exponent(number);
63    let bits = if exponent < 0 {
64        if exponent <= -SIGNIFICAND_SIZE {
65            return 0;
66        }
67
68        significand(number) >> -exponent
69    } else {
70        if exponent > 31 {
71            return 0;
72        }
73
74        (significand(number) << exponent) & 0xFFFFFFFF
75    };
76
77    (sign(number) * (bits as i64)) as i32
78}
79
80/// Converts a 64-bit floating point number to an `u32` according to the [`ToUint32`][ToUint32] algorithm.
81///
82/// [ToUint32]: https://tc39.es/ecma262/#sec-touint32
83#[inline]
84pub(crate) fn f64_to_uint32(number: f64) -> u32 {
85    f64_to_int32(number) as u32
86}