oxidd_rules_mtbdd/terminal/
f64.rs

1use std::fmt;
2use std::fmt::Display;
3use std::hash::Hash;
4use std::ops::{Add, Div, Mul, Sub};
5use std::str::FromStr;
6
7use oxidd_core::function::NumberBase;
8
9/// [`f64`], but strongly normalizing and thus implementing [`Eq`] and [`Hash`].
10/// In particular, `Float64::nan() == Float64::nan()` holds. Internally, all NaN
11/// values are normalized to [`f64::NAN`] and `-0.0` is normalized to `0.0`.
12#[derive(Clone, Copy)]
13pub struct F64(f64);
14
15impl PartialEq for F64 {
16    #[inline]
17    fn eq(&self, other: &Self) -> bool {
18        self.0.to_bits() == other.0.to_bits()
19    }
20}
21impl Eq for F64 {}
22impl PartialOrd for F64 {
23    #[inline]
24    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
25        if self.0.to_bits() == f64::NAN.to_bits() {
26            if other.0.to_bits() == f64::NAN.to_bits() {
27                Some(std::cmp::Ordering::Equal)
28            } else {
29                None
30            }
31        } else {
32            self.0.partial_cmp(&other.0)
33        }
34    }
35}
36
37impl Hash for F64 {
38    #[inline]
39    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
40        self.0.to_bits().hash(state);
41    }
42}
43
44impl From<f64> for F64 {
45    #[inline]
46    fn from(value: f64) -> Self {
47        Self(if value.is_nan() {
48            f64::NAN
49        } else if value.to_bits() == (-0.0f64).to_bits() {
50            0.0
51        } else {
52            value
53        })
54    }
55}
56impl From<F64> for f64 {
57    #[inline(always)]
58    fn from(value: F64) -> Self {
59        value.0
60    }
61}
62
63impl NumberBase for F64 {
64    #[inline]
65    fn zero() -> Self {
66        Self(0.)
67    }
68    #[inline]
69    fn one() -> Self {
70        Self(1.)
71    }
72    #[inline]
73    fn nan() -> Self {
74        Self(f64::NAN)
75    }
76
77    #[inline]
78    fn add(&self, rhs: &Self) -> Self {
79        Self::from(self.0 + rhs.0)
80    }
81    #[inline]
82    fn sub(&self, rhs: &Self) -> Self {
83        Self::from(self.0 - rhs.0)
84    }
85    #[inline]
86    fn mul(&self, rhs: &Self) -> Self {
87        Self::from(self.0 * rhs.0)
88    }
89    #[inline]
90    fn div(&self, rhs: &Self) -> Self {
91        Self::from(self.0 / rhs.0)
92    }
93}
94
95impl<Tag: Default> oxidd_dump::ParseTagged<Tag> for F64 {
96    fn parse(s: &str) -> Option<(Self, Tag)> {
97        let val = match s {
98            "nan" | "NaN" | "NAN" => Self(f64::NAN),
99            "-∞" | "-inf" | "-infinity" | "-Inf" | "-Infinity" | "-INF" | "-INFINITY"
100            | "MinusInf" => Self(f64::NEG_INFINITY),
101            "∞" | "inf" | "infinity" | "Inf" | "Infinity" | "INF" | "INFINITY" | "+∞" | "+inf"
102            | "+infinity" | "+Inf" | "+Infinity" | "+INF" | "+INFINITY" | "PlusInf" => {
103                Self(f64::INFINITY)
104            }
105            _ => Self(f64::from_str(s).ok()?),
106        };
107        Some((val, Tag::default()))
108    }
109}
110
111impl Display for F64 {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        if self.0.to_bits() == f64::NAN.to_bits() {
114            f.write_str("NaN")
115        } else if self.0.to_bits() == f64::NEG_INFINITY.to_bits() {
116            f.write_str("-∞")
117        } else if self.0.to_bits() == f64::INFINITY.to_bits() {
118            f.write_str("+∞")
119        } else {
120            self.0.fmt(f)
121        }
122    }
123}
124impl fmt::Debug for F64 {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        Display::fmt(self, f)
127    }
128}
129
130impl oxidd_dump::AsciiDisplay for F64 {
131    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132        if self.0.to_bits() == f64::NAN.to_bits() {
133            f.write_str("NaN")
134        } else if self.0.to_bits() == f64::NEG_INFINITY.to_bits() {
135            f.write_str("-INF")
136        } else if self.0.to_bits() == f64::INFINITY.to_bits() {
137            f.write_str("+INF")
138        } else {
139            self.0.fmt(f)
140        }
141    }
142}
143
144impl Add for F64 {
145    type Output = F64;
146
147    #[inline]
148    fn add(self, rhs: Self) -> F64 {
149        Self::from(self.0 + rhs.0)
150    }
151}
152
153impl Sub for F64 {
154    type Output = F64;
155
156    #[inline]
157    fn sub(self, rhs: Self) -> F64 {
158        Self::from(self.0 - rhs.0)
159    }
160}
161
162impl Mul for F64 {
163    type Output = F64;
164
165    #[inline]
166    fn mul(self, rhs: Self) -> F64 {
167        Self::from(self.0 * rhs.0)
168    }
169}
170
171impl Div for F64 {
172    type Output = F64;
173
174    #[inline]
175    fn div(self, rhs: Self) -> F64 {
176        Self::from(self.0 / rhs.0)
177    }
178}
179
180super::impl_ref_op!(F64, Add, add);
181super::impl_ref_op!(F64, Sub, sub);
182super::impl_ref_op!(F64, Mul, mul);
183super::impl_ref_op!(F64, Div, div);
184
185#[cfg(test)]
186mod test {
187    use oxidd_core::function::NumberBase;
188
189    use super::F64;
190
191    #[test]
192    fn nan_eq_ord() {
193        let nan = F64::nan();
194        assert!(nan <= nan);
195        assert!(nan == nan);
196        assert!(nan >= nan);
197    }
198
199    #[test]
200    fn zero_ord() {
201        assert!(F64::from(-0.0) <= F64::from(0.0));
202        assert!(F64::from(-0.0) == F64::from(0.0));
203        assert!(F64::from(-0.0) >= F64::from(0.0));
204    }
205}