implicit_predicates_oracle/dyadic.rs
1//! Exact dyadic rational numbers, `mantissa * 2^exponent`.
2
3use core::cmp::Ordering;
4use core::ops::{Add, Mul, Neg, Sub};
5
6use num_bigint::{BigInt, Sign};
7
8/// An exact dyadic rational: an arbitrary-precision integer mantissa times a
9/// power of two.
10///
11/// Every finite `f64` converts exactly, and addition, subtraction and
12/// multiplication never round or overflow. Equality and ordering compare
13/// values, not representations.
14///
15/// # Examples
16///
17/// ```
18/// use implicit_predicates_oracle::Dyadic;
19///
20/// let tenth = Dyadic::from_f64(0.1);
21/// let three_tenths = &(&tenth + &tenth) + &tenth;
22/// // 0.1 + 0.1 + 0.1 is not 0.3 in binary, and the oracle sees the difference.
23/// assert_eq!(three_tenths.sign(), 1);
24/// assert!(three_tenths > Dyadic::from_f64(0.3));
25/// ```
26#[derive(Clone, Debug)]
27pub struct Dyadic {
28 mant: BigInt,
29 exp: i64,
30}
31
32impl Dyadic {
33 /// Converts a finite `f64` exactly. Negative zero converts to zero.
34 ///
35 /// # Panics
36 ///
37 /// Panics if `x` is NaN or infinite.
38 ///
39 /// # Examples
40 ///
41 /// ```
42 /// use implicit_predicates_oracle::Dyadic;
43 ///
44 /// let two = &Dyadic::from_f64(1.0) + &Dyadic::from_f64(1.0);
45 /// assert_eq!(two, Dyadic::from_f64(2.0));
46 /// ```
47 pub fn from_f64(x: f64) -> Dyadic {
48 assert!(x.is_finite(), "Dyadic::from_f64: non-finite input");
49 let bits = x.to_bits();
50 let biased_exp = ((bits >> 52) & 0x7ff) as i64;
51 let fraction = bits & ((1u64 << 52) - 1);
52 let (magnitude, exp) = if biased_exp == 0 {
53 // Subnormal (or zero): no implicit bit.
54 (fraction, -1074)
55 } else {
56 (fraction | (1u64 << 52), biased_exp - 1075)
57 };
58 let sign = if bits >> 63 == 1 {
59 Sign::Minus
60 } else {
61 Sign::Plus
62 };
63 Dyadic {
64 mant: BigInt::from_biguint(sign, magnitude.into()),
65 exp,
66 }
67 }
68
69 /// Returns the exact sum of all the given values.
70 ///
71 /// # Panics
72 ///
73 /// Panics if any value is NaN or infinite.
74 ///
75 /// # Examples
76 ///
77 /// ```
78 /// use implicit_predicates_oracle::Dyadic;
79 ///
80 /// assert_eq!(Dyadic::sum_of(&[1.0, 2.0, 4.0]), Dyadic::from_f64(7.0));
81 /// assert!(Dyadic::sum_of(&[]).is_zero());
82 /// ```
83 pub fn sum_of(xs: &[f64]) -> Dyadic {
84 xs.iter()
85 .fold(Dyadic::from_f64(0.0), |acc, &x| &acc + &Dyadic::from_f64(x))
86 }
87
88 /// Returns `-1`, `0` or `1` according to the sign of the value.
89 ///
90 /// # Examples
91 ///
92 /// ```
93 /// use implicit_predicates_oracle::Dyadic;
94 ///
95 /// assert_eq!(Dyadic::from_f64(-3.5).sign(), -1);
96 /// assert_eq!(Dyadic::from_f64(0.0).sign(), 0);
97 /// assert_eq!(Dyadic::from_f64(3.5).sign(), 1);
98 /// ```
99 pub fn sign(&self) -> i8 {
100 match self.mant.sign() {
101 Sign::Minus => -1,
102 Sign::NoSign => 0,
103 Sign::Plus => 1,
104 }
105 }
106
107 /// Returns `true` if the value is exactly zero.
108 ///
109 /// # Examples
110 ///
111 /// ```
112 /// use implicit_predicates_oracle::Dyadic;
113 ///
114 /// assert!(Dyadic::from_f64(-0.0).is_zero());
115 /// assert!(!Dyadic::from_f64(1e-300).is_zero());
116 /// ```
117 pub fn is_zero(&self) -> bool {
118 self.sign() == 0
119 }
120
121 /// Mantissa scaled so that it is expressed with exponent `exp`, which
122 /// must not exceed `self.exp`.
123 fn mantissa_at(&self, exp: i64) -> BigInt {
124 let shift = usize::try_from(self.exp - exp).expect("exponent difference fits in usize");
125 &self.mant << shift
126 }
127}
128
129impl Add for &Dyadic {
130 type Output = Dyadic;
131
132 fn add(self, rhs: &Dyadic) -> Dyadic {
133 let exp = self.exp.min(rhs.exp);
134 Dyadic {
135 mant: self.mantissa_at(exp) + rhs.mantissa_at(exp),
136 exp,
137 }
138 }
139}
140
141impl Sub for &Dyadic {
142 type Output = Dyadic;
143
144 fn sub(self, rhs: &Dyadic) -> Dyadic {
145 let exp = self.exp.min(rhs.exp);
146 Dyadic {
147 mant: self.mantissa_at(exp) - rhs.mantissa_at(exp),
148 exp,
149 }
150 }
151}
152
153impl Mul for &Dyadic {
154 type Output = Dyadic;
155
156 fn mul(self, rhs: &Dyadic) -> Dyadic {
157 Dyadic {
158 mant: &self.mant * &rhs.mant,
159 exp: self.exp + rhs.exp,
160 }
161 }
162}
163
164impl Neg for &Dyadic {
165 type Output = Dyadic;
166
167 fn neg(self) -> Dyadic {
168 Dyadic {
169 mant: -&self.mant,
170 exp: self.exp,
171 }
172 }
173}
174
175/// Implements an operator for owned operands by delegating to the
176/// reference implementation.
177macro_rules! forward_owned_binary_op {
178 ($trait:ident, $method:ident) => {
179 impl $trait for Dyadic {
180 type Output = Dyadic;
181
182 fn $method(self, rhs: Dyadic) -> Dyadic {
183 $trait::$method(&self, &rhs)
184 }
185 }
186 };
187}
188
189forward_owned_binary_op!(Add, add);
190forward_owned_binary_op!(Sub, sub);
191forward_owned_binary_op!(Mul, mul);
192
193impl Neg for Dyadic {
194 type Output = Dyadic;
195
196 fn neg(self) -> Dyadic {
197 -&self
198 }
199}
200
201impl PartialEq for Dyadic {
202 fn eq(&self, other: &Dyadic) -> bool {
203 self.cmp(other) == Ordering::Equal
204 }
205}
206
207impl Eq for Dyadic {}
208
209impl PartialOrd for Dyadic {
210 fn partial_cmp(&self, other: &Dyadic) -> Option<Ordering> {
211 Some(self.cmp(other))
212 }
213}
214
215impl Ord for Dyadic {
216 fn cmp(&self, other: &Dyadic) -> Ordering {
217 (self - other).sign().cmp(&0)
218 }
219}