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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// devela::num::dom::real::float::bits
//
//! Defines [`f32bits`]|`_niche`, [`f64bits`]|`_niche`.
//
#![allow(non_camel_case_types)]
use crate::{ConstInit, NonExtremeU32, NonExtremeU64, ne, paste};
// Macro helper to implement the types f32bits, f32bits_niche, ...
macro_rules! impl_fbits {
() => { impl_fbits![f32+u32, f64+u64]; };
($($float:ident + $bits:ident),+ $(,)? ) => { paste! { $( impl_fbits![
@[<$float bits>], [<$float bits_niche>], $float, $bits, [<NonExtreme $bits:upper>]
]; )+ }};
(@$non_niche:ident, $niche:ident, $float:ident, $bits:ident, $NE:ident) => { paste! {
/* non-niche */
#[doc = crate::_tags!(num ffi)]
#[doc = "Bitwise wrapper for `" $float "` providing `Eq`, `Ord`, and `Hash`."]
#[doc = crate::_doc_location!("num/dom/real")]
///
#[doc = "This stores the raw IEEE-754 bits of a `" $float "` in a `" $bits "`."]
/// Ordering and hashing operate on the raw bit pattern, not the numeric value.
/// All bit patterns are preserved, including NaNs and signed zeroes.
///
#[doc = "For a masked and niche-compressed variant, see [`" $niche "`]."]
#[must_use]
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct $non_niche($bits);
impl $non_niche {
/// Converts a `$float` into its raw-bit representation.
pub const fn new(float: $float) -> Self { Self(float.to_bits()) }
/// Wraps an existing raw bit pattern.
pub const fn from_bits(bits: $bits) -> Self { Self(bits) }
/// Reinterprets the stored bits as a `$float`.
pub const fn as_float(self) -> $float { <$float>::from_bits(self.0) }
/// Returns the underlying raw bits unchanged.
pub const fn as_bits(self) -> $bits { self.0 }
/// Converts to the niche variant.
pub const fn to_niche(self) -> $niche { $niche::from_bits(self.as_bits()) }
}
impl Default for $non_niche {
/// The default value is the bit pattern of `0.0`.
fn default() -> Self { Self::new(0.0) }
}
impl ConstInit for $non_niche {
/// The initialization value is the bit pattern of `0.0`.
const INIT: Self = Self::new(0.0);
}
impl From<$float> for $non_niche {
fn from(from: $float) -> $non_niche { $non_niche::new(from) }
}
impl From<$non_niche> for $float {
fn from(from: $non_niche) -> $float { from.as_float() }
}
/* niche */
#[doc = crate::_tags!(num niche)]
#[doc = "Bitwise wrapper for `" $float "` stored through a masked [`" $NE "`]."]
#[doc = crate::_doc_location!("num/dom/real")]
///
/// This preserves all IEEE-754 bit patterns except the prohibited value.
/// Ordering and hashing operate on the masked bit pattern.
/// Signed zeroes and all NaNs are represented, with a single NaN payload
/// (the prohibited one) mapped to an adjacent payload.
///
/// # Reserved NaN Payload
#[doc = "The only prohibited bit pattern is `" $bits "::MAX`."]
///
/// It is a quiet NaN that also matches the highest value of the
/// underlying unsigned integer type. This makes it a good sentinel:
/// it never appears in normal floating-point or millisecond values.
///
/// If this bit pattern is used, it is automatically replaced with
/// the previous representable payload, keeping every other value intact.
///
#[doc = "For the unmasked, fully identity-preserving variant, see [`" $non_niche "`]."]
#[must_use]
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct $niche($NE);
impl $niche {
#[doc = "Converts a `" $float "` into its masked bit representation."]
///
/// All bit patterns are preserved except the prohibited one.
pub const fn new(float: $float) -> Self {
Self(ne![lossy float.to_bits(), $bits])
}
#[doc = "Wraps an existing raw bit pattern, masking it through `" $NE "`."]
pub const fn from_bits(bits: $bits) -> Self {
Self(ne![lossy bits, $bits])
}
#[doc = "Reinterprets the stored bits as a `" $float "`."]
pub const fn as_float(self) -> $float { <$float>::from_bits(self.as_bits()) }
/// Returns the masked raw bits.
///
/// The prohibited value is never returned; it is always replaced
#[doc = "by the remapped payload defined by `" $NE "`."]
pub const fn as_bits(self) -> $bits { self.0.get() }
/// Converts to the non-niche variant.
pub const fn to_non_niche(self) -> $non_niche { $non_niche::from_bits(self.as_bits()) }
}
impl Default for $niche {
/// The default value is the bit pattern of `0.0`.
fn default() -> Self { Self::new(0.0) }
}
impl ConstInit for $niche {
/// The initialization value is the bit pattern of `0.0`.
const INIT: Self = Self::new(0.0);
}
impl From<$float> for $niche {
fn from(from: $float) -> $niche { $niche::new(from) }
}
impl From<$niche> for $float {
fn from(from: $niche) -> $float { from.as_float() }
}
}};
}
impl_fbits!();