nonbox/
f64.rs

1//! NaN boxing for `f64`.
2
3const EXPONENT_MASK_OFFSET: usize = 48;
4const SIGN_MASK: u64 = 1 << 63;
5const EXPONENT_MASK: u64 = 0x7ffc << EXPONENT_MASK_OFFSET;
6const PAYLOAD_MASK: u64 = !(0xfffc << EXPONENT_MASK_OFFSET);
7
8/// Boxes a 50-bit unsigned integer.
9#[inline]
10pub const fn box_unsigned(payload: u64) -> u64 {
11    EXPONENT_MASK | payload
12}
13
14/// Unboxes a 50-bit unsigned integer.
15#[inline]
16pub const fn unbox_unsigned(number: u64) -> Option<u64> {
17    if is_boxed(number) {
18        Some(unbox_unsigned_unchecked(number))
19    } else {
20        None
21    }
22}
23
24/// Unboxes a 50-bit unsigned integer without any type check.
25#[inline]
26pub const fn unbox_unsigned_unchecked(number: u64) -> u64 {
27    number & PAYLOAD_MASK
28}
29
30/// Boxes a 51-bit signed integer.
31#[inline]
32pub const fn box_signed(payload: i64) -> u64 {
33    (if payload < 0 { SIGN_MASK } else { 0 }) | box_unsigned(payload.unsigned_abs())
34}
35
36/// Unboxes a 51-bit signed integer.
37#[inline]
38pub const fn unbox_signed(number: u64) -> Option<i64> {
39    if let Some(value) = unbox_unsigned(number) {
40        Some((if number & SIGN_MASK == 0 { 1 } else { -1 }) * value as i64)
41    } else {
42        None
43    }
44}
45
46/// Returns `true` if a payload is boxed in a given number.
47#[inline]
48pub const fn is_boxed(number: u64) -> bool {
49    number & EXPONENT_MASK == EXPONENT_MASK
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    const MAXIMUM: i64 = 1 << 50;
57
58    #[test]
59    fn check_mask() {
60        assert_ne!(EXPONENT_MASK, f64::NAN.to_bits());
61        assert!(f64::from_bits(EXPONENT_MASK).is_nan());
62    }
63
64    #[test]
65    fn unbox_nan() {
66        assert_eq!(unbox_signed(f64::NAN.to_bits()), None);
67        assert_eq!(unbox_signed(f64::INFINITY.to_bits()), None);
68        assert_eq!(unbox_signed(f64::NEG_INFINITY.to_bits()), None);
69    }
70
71    #[test]
72    fn box_unsigned_value() {
73        fn box_to_f64(payload: u64) -> f64 {
74            f64::from_bits(box_unsigned(payload))
75        }
76
77        assert!(box_to_f64(0).is_nan());
78        assert!(box_to_f64(1).is_nan());
79        assert!(box_to_f64(7).is_nan());
80        assert!(box_to_f64(42).is_nan());
81    }
82
83    #[test]
84    fn unbox_unsigned_value() {
85        assert_eq!(unbox_unsigned(42.0f64.to_bits()), None);
86        assert_eq!(unbox_unsigned(box_unsigned(0)), Some(0));
87        assert_eq!(unbox_unsigned(box_unsigned(1)), Some(1));
88        assert_eq!(unbox_unsigned(box_unsigned(7)), Some(7));
89        assert_eq!(unbox_unsigned(box_unsigned(42)), Some(42));
90    }
91
92    #[test]
93    fn unsigned_maximum() {
94        let x = MAXIMUM as _;
95
96        assert_eq!(unbox_unsigned(box_unsigned(x - 1)), Some(x - 1));
97        assert_eq!(unbox_unsigned(box_unsigned(x)), Some(0));
98    }
99
100    #[test]
101    fn box_signed_value() {
102        fn box_to_f64(payload: i64) -> f64 {
103            f64::from_bits(box_signed(payload))
104        }
105
106        assert!(box_to_f64(0).is_nan());
107        assert!(box_to_f64(1).is_nan());
108        assert!(box_to_f64(7).is_nan());
109        assert!(box_to_f64(42).is_nan());
110        assert!(box_to_f64(-1).is_nan());
111        assert!(box_to_f64(-7).is_nan());
112        assert!(box_to_f64(-42).is_nan());
113    }
114
115    #[test]
116    fn unbox_signed_value() {
117        assert_eq!(unbox_signed(42.0f64.to_bits()), None);
118        assert_eq!(unbox_signed(box_signed(0)), Some(0));
119        assert_eq!(unbox_signed(box_signed(1)), Some(1));
120        assert_eq!(unbox_signed(box_signed(7)), Some(7));
121        assert_eq!(unbox_signed(box_signed(42)), Some(42));
122        assert_eq!(unbox_signed(box_signed(-1)), Some(-1));
123        assert_eq!(unbox_signed(box_signed(-7)), Some(-7));
124        assert_eq!(unbox_signed(box_signed(-42)), Some(-42));
125    }
126
127    #[test]
128    fn signed_maximum() {
129        assert_eq!(unbox_signed(box_signed(MAXIMUM - 1)), Some(MAXIMUM - 1));
130        assert_eq!(unbox_signed(box_signed(MAXIMUM)), Some(0));
131    }
132
133    #[test]
134    fn signed_minimum() {
135        assert_eq!(unbox_signed(box_signed(1 - MAXIMUM)), Some(1 - MAXIMUM));
136        assert_eq!(unbox_signed(box_signed(-MAXIMUM)), Some(0));
137    }
138
139    #[test]
140    fn unbox_f64_value() {
141        fn unbox_from_f64(number: f64) -> Option<u64> {
142            unbox_unsigned(number.to_bits())
143        }
144
145        assert_eq!(unbox_from_f64(0.0), None);
146        assert_eq!(unbox_from_f64(-1.0), None);
147        assert_eq!(unbox_from_f64(1.0), None);
148        assert_eq!(unbox_from_f64(42.0), None);
149    }
150}