]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_apfloat/src/ppc.rs
Rollup merge of #97805 - coolreader18:trace-suggestions, r=oli-obk
[rust.git] / compiler / rustc_apfloat / src / ppc.rs
1 use crate::ieee;
2 use crate::{Category, ExpInt, Float, FloatConvert, ParseError, Round, Status, StatusAnd};
3
4 use core::cmp::Ordering;
5 use core::fmt;
6 use core::ops::Neg;
7
8 #[must_use]
9 #[derive(Copy, Clone, PartialEq, PartialOrd, Debug)]
10 pub struct DoubleFloat<F>(F, F);
11 pub type DoubleDouble = DoubleFloat<ieee::Double>;
12
13 // These are legacy semantics for the Fallback, inaccurate implementation of
14 // IBM double-double, if the accurate DoubleDouble doesn't handle the
15 // operation. It's equivalent to having an IEEE number with consecutive 106
16 // bits of mantissa and 11 bits of exponent.
17 //
18 // It's not equivalent to IBM double-double. For example, a legit IBM
19 // double-double, 1 + epsilon:
20 //
21 //   1 + epsilon = 1 + (1 >> 1076)
22 //
23 // is not representable by a consecutive 106 bits of mantissa.
24 //
25 // Currently, these semantics are used in the following way:
26 //
27 //   DoubleDouble -> (Double, Double) ->
28 //   DoubleDouble's Fallback -> IEEE operations
29 //
30 // FIXME: Implement all operations in DoubleDouble, and delete these
31 // semantics.
32 // FIXME(eddyb) This shouldn't need to be `pub`, it's only used in bounds.
33 pub struct FallbackS<F>(F);
34 type Fallback<F> = ieee::IeeeFloat<FallbackS<F>>;
35 impl<F: Float> ieee::Semantics for FallbackS<F> {
36     // Forbid any conversion to/from bits.
37     const BITS: usize = 0;
38     const PRECISION: usize = F::PRECISION * 2;
39     const MAX_EXP: ExpInt = F::MAX_EXP as ExpInt;
40     const MIN_EXP: ExpInt = F::MIN_EXP as ExpInt + F::PRECISION as ExpInt;
41 }
42
43 // Convert number to F. To avoid spurious underflows, we re-
44 // normalize against the F exponent range first, and only *then*
45 // truncate the mantissa. The result of that second conversion
46 // may be inexact, but should never underflow.
47 // FIXME(eddyb) This shouldn't need to be `pub`, it's only used in bounds.
48 pub struct FallbackExtendedS<F>(F);
49 type FallbackExtended<F> = ieee::IeeeFloat<FallbackExtendedS<F>>;
50 impl<F: Float> ieee::Semantics for FallbackExtendedS<F> {
51     // Forbid any conversion to/from bits.
52     const BITS: usize = 0;
53     const PRECISION: usize = Fallback::<F>::PRECISION;
54     const MAX_EXP: ExpInt = F::MAX_EXP as ExpInt;
55 }
56
57 impl<F: Float> From<Fallback<F>> for DoubleFloat<F>
58 where
59     F: FloatConvert<FallbackExtended<F>>,
60     FallbackExtended<F>: FloatConvert<F>,
61 {
62     fn from(x: Fallback<F>) -> Self {
63         let mut status;
64         let mut loses_info = false;
65
66         let extended: FallbackExtended<F> = unpack!(status=, x.convert(&mut loses_info));
67         assert_eq!((status, loses_info), (Status::OK, false));
68
69         let a = unpack!(status=, extended.convert(&mut loses_info));
70         assert_eq!(status - Status::INEXACT, Status::OK);
71
72         // If conversion was exact or resulted in a special case, we're done;
73         // just set the second double to zero. Otherwise, re-convert back to
74         // the extended format and compute the difference. This now should
75         // convert exactly to double.
76         let b = if a.is_finite_non_zero() && loses_info {
77             let u: FallbackExtended<F> = unpack!(status=, a.convert(&mut loses_info));
78             assert_eq!((status, loses_info), (Status::OK, false));
79             let v = unpack!(status=, extended - u);
80             assert_eq!(status, Status::OK);
81             let v = unpack!(status=, v.convert(&mut loses_info));
82             assert_eq!((status, loses_info), (Status::OK, false));
83             v
84         } else {
85             F::ZERO
86         };
87
88         DoubleFloat(a, b)
89     }
90 }
91
92 impl<F: FloatConvert<Self>> From<DoubleFloat<F>> for Fallback<F> {
93     fn from(DoubleFloat(a, b): DoubleFloat<F>) -> Self {
94         let mut status;
95         let mut loses_info = false;
96
97         // Get the first F and convert to our format.
98         let a = unpack!(status=, a.convert(&mut loses_info));
99         assert_eq!((status, loses_info), (Status::OK, false));
100
101         // Unless we have a special case, add in second F.
102         if a.is_finite_non_zero() {
103             let b = unpack!(status=, b.convert(&mut loses_info));
104             assert_eq!((status, loses_info), (Status::OK, false));
105
106             (a + b).value
107         } else {
108             a
109         }
110     }
111 }
112
113 float_common_impls!(DoubleFloat<F>);
114
115 impl<F: Float> Neg for DoubleFloat<F> {
116     type Output = Self;
117     fn neg(self) -> Self {
118         if self.1.is_finite_non_zero() {
119             DoubleFloat(-self.0, -self.1)
120         } else {
121             DoubleFloat(-self.0, self.1)
122         }
123     }
124 }
125
126 impl<F: FloatConvert<Fallback<F>>> fmt::Display for DoubleFloat<F> {
127     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128         fmt::Display::fmt(&Fallback::from(*self), f)
129     }
130 }
131
132 impl<F: FloatConvert<Fallback<F>>> Float for DoubleFloat<F>
133 where
134     Self: From<Fallback<F>>,
135 {
136     const BITS: usize = F::BITS * 2;
137     const PRECISION: usize = Fallback::<F>::PRECISION;
138     const MAX_EXP: ExpInt = Fallback::<F>::MAX_EXP;
139     const MIN_EXP: ExpInt = Fallback::<F>::MIN_EXP;
140
141     const ZERO: Self = DoubleFloat(F::ZERO, F::ZERO);
142
143     const INFINITY: Self = DoubleFloat(F::INFINITY, F::ZERO);
144
145     // FIXME(eddyb) remove when qnan becomes const fn.
146     const NAN: Self = DoubleFloat(F::NAN, F::ZERO);
147
148     fn qnan(payload: Option<u128>) -> Self {
149         DoubleFloat(F::qnan(payload), F::ZERO)
150     }
151
152     fn snan(payload: Option<u128>) -> Self {
153         DoubleFloat(F::snan(payload), F::ZERO)
154     }
155
156     fn largest() -> Self {
157         let status;
158         let mut r = DoubleFloat(F::largest(), F::largest());
159         r.1 = r.1.scalbn(-(F::PRECISION as ExpInt + 1));
160         r.1 = unpack!(status=, r.1.next_down());
161         assert_eq!(status, Status::OK);
162         r
163     }
164
165     const SMALLEST: Self = DoubleFloat(F::SMALLEST, F::ZERO);
166
167     fn smallest_normalized() -> Self {
168         DoubleFloat(F::smallest_normalized().scalbn(F::PRECISION as ExpInt), F::ZERO)
169     }
170
171     // Implement addition, subtraction, multiplication and division based on:
172     // "Software for Doubled-Precision Floating-Point Computations",
173     // by Seppo Linnainmaa, ACM TOMS vol 7 no 3, September 1981, pages 272-283.
174
175     fn add_r(mut self, rhs: Self, round: Round) -> StatusAnd<Self> {
176         match (self.category(), rhs.category()) {
177             (Category::Infinity, Category::Infinity) => {
178                 if self.is_negative() != rhs.is_negative() {
179                     Status::INVALID_OP.and(Self::NAN.copy_sign(self))
180                 } else {
181                     Status::OK.and(self)
182                 }
183             }
184
185             (_, Category::Zero) | (Category::NaN, _) | (Category::Infinity, Category::Normal) => {
186                 Status::OK.and(self)
187             }
188
189             (Category::Zero, _) | (_, Category::NaN | Category::Infinity) => Status::OK.and(rhs),
190
191             (Category::Normal, Category::Normal) => {
192                 let mut status = Status::OK;
193                 let (a, aa, c, cc) = (self.0, self.1, rhs.0, rhs.1);
194                 let mut z = a;
195                 z = unpack!(status|=, z.add_r(c, round));
196                 if !z.is_finite() {
197                     if !z.is_infinite() {
198                         return status.and(DoubleFloat(z, F::ZERO));
199                     }
200                     status = Status::OK;
201                     let a_cmp_c = a.cmp_abs_normal(c);
202                     z = cc;
203                     z = unpack!(status|=, z.add_r(aa, round));
204                     if a_cmp_c == Ordering::Greater {
205                         // z = cc + aa + c + a;
206                         z = unpack!(status|=, z.add_r(c, round));
207                         z = unpack!(status|=, z.add_r(a, round));
208                     } else {
209                         // z = cc + aa + a + c;
210                         z = unpack!(status|=, z.add_r(a, round));
211                         z = unpack!(status|=, z.add_r(c, round));
212                     }
213                     if !z.is_finite() {
214                         return status.and(DoubleFloat(z, F::ZERO));
215                     }
216                     self.0 = z;
217                     let mut zz = aa;
218                     zz = unpack!(status|=, zz.add_r(cc, round));
219                     if a_cmp_c == Ordering::Greater {
220                         // self.1 = a - z + c + zz;
221                         self.1 = a;
222                         self.1 = unpack!(status|=, self.1.sub_r(z, round));
223                         self.1 = unpack!(status|=, self.1.add_r(c, round));
224                         self.1 = unpack!(status|=, self.1.add_r(zz, round));
225                     } else {
226                         // self.1 = c - z + a + zz;
227                         self.1 = c;
228                         self.1 = unpack!(status|=, self.1.sub_r(z, round));
229                         self.1 = unpack!(status|=, self.1.add_r(a, round));
230                         self.1 = unpack!(status|=, self.1.add_r(zz, round));
231                     }
232                 } else {
233                     // q = a - z;
234                     let mut q = a;
235                     q = unpack!(status|=, q.sub_r(z, round));
236
237                     // zz = q + c + (a - (q + z)) + aa + cc;
238                     // Compute a - (q + z) as -((q + z) - a) to avoid temporary copies.
239                     let mut zz = q;
240                     zz = unpack!(status|=, zz.add_r(c, round));
241                     q = unpack!(status|=, q.add_r(z, round));
242                     q = unpack!(status|=, q.sub_r(a, round));
243                     q = -q;
244                     zz = unpack!(status|=, zz.add_r(q, round));
245                     zz = unpack!(status|=, zz.add_r(aa, round));
246                     zz = unpack!(status|=, zz.add_r(cc, round));
247                     if zz.is_zero() && !zz.is_negative() {
248                         return Status::OK.and(DoubleFloat(z, F::ZERO));
249                     }
250                     self.0 = z;
251                     self.0 = unpack!(status|=, self.0.add_r(zz, round));
252                     if !self.0.is_finite() {
253                         self.1 = F::ZERO;
254                         return status.and(self);
255                     }
256                     self.1 = z;
257                     self.1 = unpack!(status|=, self.1.sub_r(self.0, round));
258                     self.1 = unpack!(status|=, self.1.add_r(zz, round));
259                 }
260                 status.and(self)
261             }
262         }
263     }
264
265     fn mul_r(mut self, rhs: Self, round: Round) -> StatusAnd<Self> {
266         // Interesting observation: For special categories, finding the lowest
267         // common ancestor of the following layered graph gives the correct
268         // return category:
269         //
270         //    NaN
271         //   /   \
272         // Zero  Inf
273         //   \   /
274         //   Normal
275         //
276         // e.g., NaN * NaN = NaN
277         //      Zero * Inf = NaN
278         //      Normal * Zero = Zero
279         //      Normal * Inf = Inf
280         match (self.category(), rhs.category()) {
281             (Category::NaN, _) => Status::OK.and(self),
282
283             (_, Category::NaN) => Status::OK.and(rhs),
284
285             (Category::Zero, Category::Infinity) | (Category::Infinity, Category::Zero) => {
286                 Status::OK.and(Self::NAN)
287             }
288
289             (Category::Zero | Category::Infinity, _) => Status::OK.and(self),
290
291             (_, Category::Zero | Category::Infinity) => Status::OK.and(rhs),
292
293             (Category::Normal, Category::Normal) => {
294                 let mut status = Status::OK;
295                 let (a, b, c, d) = (self.0, self.1, rhs.0, rhs.1);
296                 // t = a * c
297                 let mut t = a;
298                 t = unpack!(status|=, t.mul_r(c, round));
299                 if !t.is_finite_non_zero() {
300                     return status.and(DoubleFloat(t, F::ZERO));
301                 }
302
303                 // tau = fmsub(a, c, t), that is -fmadd(-a, c, t).
304                 let mut tau = a;
305                 tau = unpack!(status|=, tau.mul_add_r(c, -t, round));
306                 // v = a * d
307                 let mut v = a;
308                 v = unpack!(status|=, v.mul_r(d, round));
309                 // w = b * c
310                 let mut w = b;
311                 w = unpack!(status|=, w.mul_r(c, round));
312                 v = unpack!(status|=, v.add_r(w, round));
313                 // tau += v + w
314                 tau = unpack!(status|=, tau.add_r(v, round));
315                 // u = t + tau
316                 let mut u = t;
317                 u = unpack!(status|=, u.add_r(tau, round));
318
319                 self.0 = u;
320                 if !u.is_finite() {
321                     self.1 = F::ZERO;
322                 } else {
323                     // self.1 = (t - u) + tau
324                     t = unpack!(status|=, t.sub_r(u, round));
325                     t = unpack!(status|=, t.add_r(tau, round));
326                     self.1 = t;
327                 }
328                 status.and(self)
329             }
330         }
331     }
332
333     fn mul_add_r(self, multiplicand: Self, addend: Self, round: Round) -> StatusAnd<Self> {
334         Fallback::from(self)
335             .mul_add_r(Fallback::from(multiplicand), Fallback::from(addend), round)
336             .map(Self::from)
337     }
338
339     fn div_r(self, rhs: Self, round: Round) -> StatusAnd<Self> {
340         Fallback::from(self).div_r(Fallback::from(rhs), round).map(Self::from)
341     }
342
343     fn c_fmod(self, rhs: Self) -> StatusAnd<Self> {
344         Fallback::from(self).c_fmod(Fallback::from(rhs)).map(Self::from)
345     }
346
347     fn round_to_integral(self, round: Round) -> StatusAnd<Self> {
348         Fallback::from(self).round_to_integral(round).map(Self::from)
349     }
350
351     fn next_up(self) -> StatusAnd<Self> {
352         Fallback::from(self).next_up().map(Self::from)
353     }
354
355     fn from_bits(input: u128) -> Self {
356         let (a, b) = (input, input >> F::BITS);
357         DoubleFloat(F::from_bits(a & ((1 << F::BITS) - 1)), F::from_bits(b & ((1 << F::BITS) - 1)))
358     }
359
360     fn from_u128_r(input: u128, round: Round) -> StatusAnd<Self> {
361         Fallback::from_u128_r(input, round).map(Self::from)
362     }
363
364     fn from_str_r(s: &str, round: Round) -> Result<StatusAnd<Self>, ParseError> {
365         Fallback::from_str_r(s, round).map(|r| r.map(Self::from))
366     }
367
368     fn to_bits(self) -> u128 {
369         self.0.to_bits() | (self.1.to_bits() << F::BITS)
370     }
371
372     fn to_u128_r(self, width: usize, round: Round, is_exact: &mut bool) -> StatusAnd<u128> {
373         Fallback::from(self).to_u128_r(width, round, is_exact)
374     }
375
376     fn cmp_abs_normal(self, rhs: Self) -> Ordering {
377         self.0.cmp_abs_normal(rhs.0).then_with(|| {
378             let result = self.1.cmp_abs_normal(rhs.1);
379             if result != Ordering::Equal {
380                 let against = self.0.is_negative() ^ self.1.is_negative();
381                 let rhs_against = rhs.0.is_negative() ^ rhs.1.is_negative();
382                 (!against)
383                     .cmp(&!rhs_against)
384                     .then_with(|| if against { result.reverse() } else { result })
385             } else {
386                 result
387             }
388         })
389     }
390
391     fn bitwise_eq(self, rhs: Self) -> bool {
392         self.0.bitwise_eq(rhs.0) && self.1.bitwise_eq(rhs.1)
393     }
394
395     fn is_negative(self) -> bool {
396         self.0.is_negative()
397     }
398
399     fn is_denormal(self) -> bool {
400         self.category() == Category::Normal
401             && (self.0.is_denormal() || self.0.is_denormal() ||
402           // (double)(Hi + Lo) == Hi defines a normal number.
403           !(self.0 + self.1).value.bitwise_eq(self.0))
404     }
405
406     fn is_signaling(self) -> bool {
407         self.0.is_signaling()
408     }
409
410     fn category(self) -> Category {
411         self.0.category()
412     }
413
414     fn get_exact_inverse(self) -> Option<Self> {
415         Fallback::from(self).get_exact_inverse().map(Self::from)
416     }
417
418     fn ilogb(self) -> ExpInt {
419         self.0.ilogb()
420     }
421
422     fn scalbn_r(self, exp: ExpInt, round: Round) -> Self {
423         DoubleFloat(self.0.scalbn_r(exp, round), self.1.scalbn_r(exp, round))
424     }
425
426     fn frexp_r(self, exp: &mut ExpInt, round: Round) -> Self {
427         let a = self.0.frexp_r(exp, round);
428         let mut b = self.1;
429         if self.category() == Category::Normal {
430             b = b.scalbn_r(-*exp, round);
431         }
432         DoubleFloat(a, b)
433     }
434 }