|
| 1 | +use super::super::fenv::{ |
| 2 | + FE_INEXACT, FE_TONEAREST, FE_UNDERFLOW, feclearexcept, fegetround, feraiseexcept, fetestexcept, |
| 3 | +}; |
| 4 | +use super::super::{CastFrom, CastInto, DFloat, Float, HFloat, IntTy, MinInt}; |
| 5 | + |
| 6 | +/// FMA implementation when a hardware-backed larger float type is available. |
| 7 | +pub fn fma_big<F, B>(x: F, y: F, z: F) -> F |
| 8 | +where |
| 9 | + F: Float + HFloat<D = B>, |
| 10 | + B: Float + DFloat<H = F>, |
| 11 | + // F: Float + CastInto<B>, |
| 12 | + // B: Float + CastInto<F> + CastFrom<F>, |
| 13 | + B::Int: CastInto<i32>, |
| 14 | + i32: CastFrom<i32>, |
| 15 | +{ |
| 16 | + let one = IntTy::<B>::ONE; |
| 17 | + |
| 18 | + let xy: B; |
| 19 | + let mut result: B; |
| 20 | + let mut ui: B::Int; |
| 21 | + let e: i32; |
| 22 | + |
| 23 | + xy = x.widen() * y.widen(); |
| 24 | + result = xy + z.widen(); |
| 25 | + ui = result.to_bits(); |
| 26 | + e = i32::cast_from(ui >> F::SIG_BITS) & F::EXP_MAX as i32; |
| 27 | + let zb: B = z.widen(); |
| 28 | + |
| 29 | + let prec_diff = B::SIG_BITS - F::SIG_BITS; |
| 30 | + let excess_prec = ui & ((one << prec_diff) - one); |
| 31 | + let x = one << (prec_diff - 1); |
| 32 | + |
| 33 | + // Common case: the larger precision is fine |
| 34 | + if excess_prec != x |
| 35 | + || e == i32::cast_from(F::EXP_MAX) |
| 36 | + || (result - xy == zb && result - zb == xy) |
| 37 | + || fegetround() != FE_TONEAREST |
| 38 | + { |
| 39 | + // TODO: feclearexcept |
| 40 | + |
| 41 | + return result.narrow(); |
| 42 | + } |
| 43 | + |
| 44 | + let neg = ui & B::SIGN_MASK > IntTy::<B>::ZERO; |
| 45 | + let err = if neg == (zb > xy) { xy - result + zb } else { zb - result + xy }; |
| 46 | + if neg == (err < B::ZERO) { |
| 47 | + ui += one; |
| 48 | + } else { |
| 49 | + ui -= one; |
| 50 | + } |
| 51 | + |
| 52 | + B::from_bits(ui).narrow() |
| 53 | +} |
0 commit comments