compiler_builtins/math/libm_math/generic/
fma_wide.rs

1use crate::support::{
2    CastFrom, CastInto, DFloat, Float, FpResult, HFloat, IntTy, MinInt, Round, Status,
3};
4
5/// Fma implementation when a hardware-backed larger float type is available. For `f32` and `f64`,
6/// `f64` has enough precision to represent the `f32` in its entirety, except for double rounding.
7#[inline]
8pub fn fma_wide_round<F, B>(x: F, y: F, z: F, round: Round) -> FpResult<F>
9where
10    F: Float + HFloat<D = B>,
11    B: Float + DFloat<H = F>,
12    B::Int: CastInto<i32>,
13    i32: CastFrom<i32>,
14{
15    let one = IntTy::<B>::ONE;
16
17    let xy: B = x.widen() * y.widen();
18    let mut result: B = xy + z.widen();
19    let mut ui: B::Int = result.to_bits();
20    let re = result.ex();
21    let zb: B = z.widen();
22
23    let prec_diff = B::SIG_BITS - F::SIG_BITS;
24    let excess_prec = ui & ((one << prec_diff) - one);
25    let halfway = one << (prec_diff - 1);
26
27    // Common case: the larger precision is fine if...
28    // This is not a halfway case
29    if excess_prec != halfway
30        // Or the result is NaN
31        || re == B::EXP_SAT
32        // Or the result is exact
33        || (result - xy == zb && result - zb == xy)
34        // Or the mode is something other than round to nearest
35        || round != Round::Nearest
36    {
37        let min_inexact_exp = (B::EXP_BIAS as i32 + F::EXP_MIN_SUBNORM) as u32;
38        let max_inexact_exp = (B::EXP_BIAS as i32 + F::EXP_MIN) as u32;
39
40        let mut status = Status::OK;
41
42        if (min_inexact_exp..max_inexact_exp).contains(&re) && status.inexact() {
43            // This branch is never hit; requires previous operations to set a status
44            status.set_inexact(false);
45
46            result = xy + z.widen();
47            if status.inexact() {
48                status.set_underflow(true);
49            } else {
50                status.set_inexact(true);
51            }
52        }
53
54        return FpResult {
55            val: result.narrow(),
56            status,
57        };
58    }
59
60    let neg = ui >> (B::BITS - 1) != IntTy::<B>::ZERO;
61    let err = if neg == (zb > xy) {
62        xy - result + zb
63    } else {
64        zb - result + xy
65    };
66    if neg == (err < B::ZERO) {
67        ui += one;
68    } else {
69        ui -= one;
70    }
71
72    FpResult::ok(B::from_bits(ui).narrow())
73}