logo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
use core::mem;

use crate::{Encoding, Nan, Primitive};

const SIGN_MASK: u64 = 0x8000_0000_0000_0000;
const EXPONENT_MASK: u64 = 0x7ff0_0000_0000_0000;
const MANTISSA_MASK: u64 = 0x000f_ffff_ffff_ffff;

const CANONICAL_NAN_BITS: u64 = 0x7ff8_0000_0000_0000;
const CANONICAL_ZERO_BITS: u64 = 0x0;

/// Converts floating-point values into a canonicalized form.
pub trait ToCanonicalBits: Encoding {
    /// Conversion to a canonical representation.
    ///
    /// Unlike the `to_bits` function provided by `f32` and `f64`, this function
    /// returns bits in a fix-sized vector (`u64`) and collapses representations
    /// for real numbers, infinities, and `NaN`s into a canonical form such that
    /// every semantic value has a unique representation as canonical bits.
    fn to_canonical_bits(self) -> u64;
}

impl<T> ToCanonicalBits for T
where
    T: Encoding + Nan + Primitive,
{
    fn to_canonical_bits(self) -> u64 {
        if self.is_nan() {
            CANONICAL_NAN_BITS
        }
        else {
            let (mantissa, exponent, sign) = self.integer_decode();
            if mantissa == 0 {
                CANONICAL_ZERO_BITS
            }
            else {
                let exponent = u64::from(unsafe { mem::transmute::<i16, u16>(exponent) });
                let sign = if sign > 0 { 1u64 } else { 0u64 };
                (mantissa & MANTISSA_MASK)
                    | ((exponent << 52) & EXPONENT_MASK)
                    | ((sign << 63) & SIGN_MASK)
            }
        }
    }
}