float_bits/
lib.rs

1//! Floats stored as raw bits, making them hashable and totally ordered.
2//!
3//! The types in this crate represent IEEE 754 binary floating point numbers, using unsigned
4//! integers to store the raw bits of the floats.  Some of these types represent float formats not
5//! supported by Rust, or only if specific Rust features are available and enabled.  A limited
6//! suite of operations are available that operate directly on the raw bit representation,
7//! bypassing the need for Rust support.
8//!
9//! # Example
10//!
11//! ```rust
12//! # use float_bits::F64;
13//!
14//! let x: f64 = 0.1;
15//! let y: F64 = x.into();
16//! let z: f64 = y.into();
17//! assert_eq!(x, z);
18//! assert_eq!(0x3fb999999999999a, y.to_bits());
19//! ```
20
21#![no_std]
22#![cfg_attr(feature = "f16", feature(f16))]
23#![cfg_attr(feature = "f128", feature(f128))]
24#![allow(missing_docs)]
25
26#[macro_use]
27mod macros;
28
29mod helpers;
30
31define!(BF16, name "BF16", unsigned u16, signed i16, with 8 exp bits, desc "a Google BFloat16 floating point number");
32define!(F16, name "F16", unsigned u16, signed i16, #[cfg(feature = "f16")] float f16, with 5 exp bits, desc "an IEEE 754 binary16 floating point number");
33define!(F32, name "F32", unsigned u32, signed i32, float f32, with 8 exp bits, desc "an IEEE 754 binary32 floating point number");
34define!(F64, name "F64", unsigned u64, signed i64, float f64, with 11 exp bits, desc "an IEEE 754 binary64 floating point number");
35define!(F128, name "F128", unsigned u128, signed i128, #[cfg(feature = "f128")] float f128, with 15 exp bits, desc "an IEEE 754 binary128 floating point number");
36
37impl core::fmt::Display for F64 {
38    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
39        let val = self.to_float();
40        core::fmt::Display::fmt(&val, f)
41    }
42}
43
44impl core::str::FromStr for F64 {
45    type Err = core::num::ParseFloatError;
46    fn from_str(s: &str) -> Result<Self, Self::Err> {
47        Ok(Self::from_float(s.parse()?))
48    }
49}
50
51impl core::fmt::Display for F32 {
52    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
53        let val = self.to_float();
54        core::fmt::Display::fmt(&val, f)
55    }
56}
57
58impl core::str::FromStr for F32 {
59    type Err = core::num::ParseFloatError;
60    fn from_str(s: &str) -> Result<Self, Self::Err> {
61        Ok(Self::from_float(s.parse()?))
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use core::num::FpCategory as Class;
69
70    #[test]
71    fn f32_smoke_test() {
72        const PZERO: F32 = F32::ZERO;
73        const NZERO: F32 = F32::NEG_ZERO;
74        const PNORM: F32 = F32::MIN_POSITIVE;
75        const NNORM: F32 = F32::MAX_NEGATIVE;
76        const PONE: F32 = F32::ONE;
77        const NONE: F32 = F32::NEG_ONE;
78        const PMAX: F32 = F32::MAX;
79        const NMAX: F32 = F32::MIN;
80        const PINF: F32 = F32::INFINITY;
81        const NINF: F32 = F32::NEG_INFINITY;
82        const PQNAN: F32 = F32::QNAN;
83        const PSNAN: F32 = F32::SNAN;
84        const NQNAN: F32 = F32::NEG_QNAN;
85        const NSNAN: F32 = F32::NEG_SNAN;
86        type Row = (u32, Class, bool, F32, f32);
87        const ROWS: [Row; 14] = [
88            (0x00000000, Class::Zero, false, PZERO, 0.0),
89            (0x80000000, Class::Zero, true, NZERO, -0.0),
90            (0x00800000, Class::Normal, false, PNORM, f32::MIN_POSITIVE),
91            (0x80800000, Class::Normal, true, NNORM, -f32::MIN_POSITIVE),
92            (0x3f800000, Class::Normal, false, PONE, 1.0),
93            (0xbf800000, Class::Normal, true, NONE, -1.0),
94            (0x7f7fffff, Class::Normal, false, PMAX, f32::MAX),
95            (0xff7fffff, Class::Normal, true, NMAX, -f32::MAX),
96            (0x7f800000, Class::Infinite, false, PINF, f32::INFINITY),
97            (0xff800000, Class::Infinite, true, NINF, f32::NEG_INFINITY),
98            (0x7f800001, Class::Nan, false, PSNAN, f32::NAN),
99            (0x7fc00001, Class::Nan, false, PQNAN, f32::NAN),
100            (0xff800001, Class::Nan, true, NSNAN, f32::NAN),
101            (0xffc00001, Class::Nan, true, NQNAN, f32::NAN),
102        ];
103        for (bits, class, neg, val, float) in ROWS {
104            assert_eq!(neg, val.is_sign_negative());
105            assert_eq!(class, val.classify());
106            assert_eq!(float.is_nan(), val.is_nan());
107            if !val.is_nan() {
108                assert_eq!(float, val.to_float());
109            }
110            assert_eq!(bits, val.to_bits());
111        }
112    }
113
114    #[test]
115    fn f64_smoke_test() {
116        const PZERO: F64 = F64::ZERO;
117        const NZERO: F64 = F64::NEG_ZERO;
118        const PNORM: F64 = F64::MIN_POSITIVE;
119        const NNORM: F64 = F64::MAX_NEGATIVE;
120        const PONE: F64 = F64::ONE;
121        const NONE: F64 = F64::NEG_ONE;
122        const PMAX: F64 = F64::MAX;
123        const NMAX: F64 = F64::MIN;
124        const PINF: F64 = F64::INFINITY;
125        const NINF: F64 = F64::NEG_INFINITY;
126        const PQNAN: F64 = F64::QNAN;
127        const PSNAN: F64 = F64::SNAN;
128        const NQNAN: F64 = F64::NEG_QNAN;
129        const NSNAN: F64 = F64::NEG_SNAN;
130        type Row = (u64, Class, bool, F64, f64);
131        const ROWS: [Row; 14] = [
132            (0x0000000000000000, Class::Zero, false, PZERO, 0.0),
133            (0x8000000000000000, Class::Zero, true, NZERO, -0.0),
134            (
135                0x0010000000000000,
136                Class::Normal,
137                false,
138                PNORM,
139                f64::MIN_POSITIVE,
140            ),
141            (
142                0x8010000000000000,
143                Class::Normal,
144                true,
145                NNORM,
146                -f64::MIN_POSITIVE,
147            ),
148            (0x3ff0000000000000, Class::Normal, false, PONE, 1.0),
149            (0xbff0000000000000, Class::Normal, true, NONE, -1.0),
150            (0x7fefffffffffffff, Class::Normal, false, PMAX, f64::MAX),
151            (0xffefffffffffffff, Class::Normal, true, NMAX, -f64::MAX),
152            (
153                0x7ff0000000000000,
154                Class::Infinite,
155                false,
156                PINF,
157                f64::INFINITY,
158            ),
159            (
160                0xfff0000000000000,
161                Class::Infinite,
162                true,
163                NINF,
164                f64::NEG_INFINITY,
165            ),
166            (0x7ff0000000000001, Class::Nan, false, PSNAN, f64::NAN),
167            (0x7ff8000000000001, Class::Nan, false, PQNAN, f64::NAN),
168            (0xfff0000000000001, Class::Nan, true, NSNAN, f64::NAN),
169            (0xfff8000000000001, Class::Nan, true, NQNAN, f64::NAN),
170        ];
171        for (bits, class, neg, val, float) in ROWS {
172            assert_eq!(neg, val.is_sign_negative());
173            assert_eq!(class, val.classify());
174            assert_eq!(float.is_nan(), val.is_nan());
175            if !val.is_nan() {
176                assert_eq!(float, val.to_float());
177            }
178            assert_eq!(bits, val.to_bits());
179        }
180    }
181}