1use num_bigint::{BigInt, Sign as BigSign};
14
15use axiolid_guarantees::Sign;
16
17use crate::arith::Arith;
18use crate::interval::Interval;
19
20#[derive(Debug, Clone, PartialEq, Eq, Hash)]
22pub struct Dyadic {
23 mantissa: BigInt,
24 exponent: i64,
25}
26
27impl Dyadic {
28 #[must_use]
30 pub fn zero() -> Self {
31 Self {
32 mantissa: BigInt::from(0),
33 exponent: 0,
34 }
35 }
36
37 #[must_use]
39 pub fn from_parts(mantissa: BigInt, exponent: i64) -> Self {
40 Self { mantissa, exponent }.normalised()
41 }
42
43 #[must_use]
45 pub fn try_from_f64(value: f64) -> Option<Self> {
46 if !value.is_finite() {
47 return None;
48 }
49 let bits = value.to_bits();
50 let negative = bits >> 63 == 1;
51 let biased = ((bits >> 52) & 0x7ff) as i64;
52 let fraction = bits & ((1u64 << 52) - 1);
53 let (magnitude, exponent) = if biased == 0 {
55 (fraction, -1074)
56 } else {
57 (fraction | (1u64 << 52), biased - 1075)
58 };
59 let mut mantissa = BigInt::from(magnitude);
60 if negative {
61 mantissa = -mantissa;
62 }
63 Some(Self::from_parts(mantissa, exponent))
64 }
65
66 #[must_use]
68 pub fn mantissa(&self) -> &BigInt {
69 &self.mantissa
70 }
71
72 #[must_use]
74 pub fn exponent(&self) -> i64 {
75 self.exponent
76 }
77
78 #[must_use]
87 pub fn enclosure(&self) -> Interval {
88 if self.mantissa.sign() == BigSign::NoSign {
89 return Interval::point(0.0);
90 }
91 let bits = self.mantissa.bits();
92 let drop = bits.saturating_sub(64);
93 let exponent = self.exponent + drop as i64;
94 if !(-1000..=900).contains(&exponent) {
96 return Interval::WHOLE;
97 }
98 let scale = 2f64.powi(exponent as i32);
99 if drop == 0 && bits <= 53 {
100 let exact = i64::try_from(&self.mantissa).expect("at most 53 bits") as f64 * scale;
105 return Interval::from_bounds(exact, exact);
106 }
107 let top = &self.mantissa >> drop;
111 let top = i128::try_from(&top).expect("at most 65 bits");
112 let low = (top as f64) * scale;
113 let high = if drop == 0 {
114 low
115 } else {
116 ((top + 1) as f64) * scale
117 };
118 Interval::from_bounds(low.next_down().next_down(), high.next_up().next_up())
119 }
120
121 #[must_use]
128 pub fn approx_parts(&self) -> (f64, i64) {
129 let bits = self.mantissa.bits();
130 if bits == 0 {
131 return (0.0, 0);
132 }
133 let drop = bits.saturating_sub(53);
134 let top = &self.mantissa >> drop;
135 let top = i64::try_from(&top).expect("at most 53 bits");
136 let mut m = top as f64;
137 let mut e = self.exponent + drop as i64;
138 let lift = 53 - bits.min(53) as i64;
141 m *= 2f64.powi(lift as i32);
142 e -= lift;
143 (m, e)
144 }
145
146 #[must_use]
148 pub fn bits(&self) -> u64 {
149 self.mantissa.bits()
150 }
151
152 #[must_use]
158 pub fn to_f64(&self) -> f64 {
159 let bits = self.mantissa.bits();
160 let drop = bits.saturating_sub(64);
161 let top = &self.mantissa >> drop;
162 let top = i128::try_from(&top).expect("at most 64 bits");
164 let exponent = self.exponent + drop as i64;
165 let exponent = exponent.clamp(-2000, 2000) as i32;
166 (top as f64) * 2f64.powi(exponent / 2) * 2f64.powi(exponent - exponent / 2)
167 }
168
169 fn normalised(mut self) -> Self {
170 match self.mantissa.trailing_zeros() {
171 None => Self::zero(),
173 Some(0) => self,
174 Some(shift) => {
175 self.mantissa >>= shift;
176 self.exponent += shift as i64;
177 self
178 }
179 }
180 }
181
182 fn aligned(&self, other: &Self) -> (BigInt, BigInt, i64) {
184 if self.exponent <= other.exponent {
185 let shift = (other.exponent - self.exponent) as u64;
186 (
187 self.mantissa.clone(),
188 &other.mantissa << shift,
189 self.exponent,
190 )
191 } else {
192 let shift = (self.exponent - other.exponent) as u64;
193 (
194 &self.mantissa << shift,
195 other.mantissa.clone(),
196 other.exponent,
197 )
198 }
199 }
200}
201
202impl Arith for Dyadic {
203 fn from_f64(value: f64) -> Self {
208 Self::try_from_f64(value).expect("exact arithmetic needs finite input")
209 }
210
211 fn from_dyadic(value: &Dyadic) -> Self {
212 value.clone()
213 }
214
215 fn add(&self, other: &Self) -> Self {
216 let (left, right, exponent) = self.aligned(other);
217 Self::from_parts(left + right, exponent)
218 }
219
220 fn sub(&self, other: &Self) -> Self {
221 let (left, right, exponent) = self.aligned(other);
222 Self::from_parts(left - right, exponent)
223 }
224
225 fn mul(&self, other: &Self) -> Self {
226 Self {
231 mantissa: &self.mantissa * &other.mantissa,
232 exponent: self.exponent + other.exponent,
233 }
234 .normalised()
235 }
236
237 fn neg(&self) -> Self {
238 Self {
239 mantissa: -&self.mantissa,
240 exponent: self.exponent,
241 }
242 }
243
244 fn sign(&self) -> Option<Sign> {
245 Some(match self.mantissa.sign() {
246 BigSign::Plus => Sign::Positive,
247 BigSign::Minus => Sign::Negative,
248 BigSign::NoSign => Sign::Zero,
249 })
250 }
251}