| 
 | 1 | +use super::super::{CastFrom, CastInto, Float, IntTy, MinInt};  | 
 | 2 | + | 
 | 3 | +/// Scale the exponent.  | 
 | 4 | +///  | 
 | 5 | +/// From N3220:  | 
 | 6 | +///  | 
 | 7 | +/// > The scalbn and scalbln functions compute `x * b^n`, where `b = FLT_RADIX` if the return type  | 
 | 8 | +/// > of the function is a standard floating type, or `b = 10` if the return type of the function  | 
 | 9 | +/// > is a decimal floating type. A range error occurs for some finite x, depending on n.  | 
 | 10 | +/// >  | 
 | 11 | +/// > [...]  | 
 | 12 | +/// >  | 
 | 13 | +/// > * `scalbn(±0, n)` returns `±0`.  | 
 | 14 | +/// > * `scalbn(x, 0)` returns `x`.  | 
 | 15 | +/// > * `scalbn(±∞, n)` returns `±∞`.  | 
 | 16 | +/// >  | 
 | 17 | +/// > If the calculation does not overflow or underflow, the returned value is exact and  | 
 | 18 | +/// > independent of the current rounding direction mode.  | 
 | 19 | +pub fn scalbn<F: Float>(mut x: F, mut n: i32) -> F  | 
 | 20 | +where  | 
 | 21 | +    u32: CastInto<F::Int>,  | 
 | 22 | +    F::Int: CastFrom<i32>,  | 
 | 23 | +    F::Int: CastFrom<u32>,  | 
 | 24 | +{  | 
 | 25 | +    let zero = IntTy::<F>::ZERO;  | 
 | 26 | + | 
 | 27 | +    // Bits including the implicit bit  | 
 | 28 | +    let sig_total_bits = F::SIG_BITS + 1;  | 
 | 29 | + | 
 | 30 | +    // Maximum and minimum values when biased  | 
 | 31 | +    let exp_max: i32 = F::EXP_BIAS as i32;  | 
 | 32 | +    let exp_min = -(exp_max - 1);  | 
 | 33 | + | 
 | 34 | +    // 2 ^ Emax, where Emax is the maximum biased exponent value (1023 for f64)  | 
 | 35 | +    let f_exp_max = F::from_parts(false, F::EXP_BIAS << 1, zero);  | 
 | 36 | + | 
 | 37 | +    // 2 ^ Emin, where Emin is the minimum biased exponent value (-1022 for f64)  | 
 | 38 | +    let f_exp_min = F::from_parts(false, 1, zero);  | 
 | 39 | + | 
 | 40 | +    // 2 ^ sig_total_bits, representation of what can be accounted for with subnormals  | 
 | 41 | +    let f_exp_subnorm = F::from_parts(false, sig_total_bits + F::EXP_BIAS, zero);  | 
 | 42 | + | 
 | 43 | +    if n > exp_max {  | 
 | 44 | +        x *= f_exp_max;  | 
 | 45 | +        n -= exp_max;  | 
 | 46 | +        if n > exp_max {  | 
 | 47 | +            x *= f_exp_max;  | 
 | 48 | +            n -= exp_max;  | 
 | 49 | +            if n > exp_max {  | 
 | 50 | +                n = exp_max;  | 
 | 51 | +            }  | 
 | 52 | +        }  | 
 | 53 | +    } else if n < exp_min {  | 
 | 54 | +        let mul = f_exp_min * f_exp_subnorm;  | 
 | 55 | +        let add = (exp_max - 1) - sig_total_bits as i32;  | 
 | 56 | + | 
 | 57 | +        x *= mul;  | 
 | 58 | +        n += add;  | 
 | 59 | +        if n < exp_min {  | 
 | 60 | +            x *= mul;  | 
 | 61 | +            n += add;  | 
 | 62 | +            if n < exp_min {  | 
 | 63 | +                n = exp_min;  | 
 | 64 | +            }  | 
 | 65 | +        }  | 
 | 66 | +    }  | 
 | 67 | + | 
 | 68 | +    x * F::from_parts(false, (F::EXP_BIAS as i32 + n) as u32, zero)  | 
 | 69 | +}  | 
 | 70 | + | 
 | 71 | +#[cfg(test)]  | 
 | 72 | +mod tests {  | 
 | 73 | +    use super::super::super::Int;  | 
 | 74 | +    use super::*;  | 
 | 75 | + | 
 | 76 | +    // Tests against N3220  | 
 | 77 | +    fn spec_test<F: Float>()  | 
 | 78 | +    where  | 
 | 79 | +        u32: CastInto<F::Int>,  | 
 | 80 | +        F::Int: CastFrom<i32>,  | 
 | 81 | +        F::Int: CastFrom<u32>,  | 
 | 82 | +    {  | 
 | 83 | +        // `scalbn(±0, n)` returns `±0`.  | 
 | 84 | +        assert_biteq!(scalbn(F::NEG_ZERO, 10), F::NEG_ZERO);  | 
 | 85 | +        assert_biteq!(scalbn(F::NEG_ZERO, 0), F::NEG_ZERO);  | 
 | 86 | +        assert_biteq!(scalbn(F::NEG_ZERO, -10), F::NEG_ZERO);  | 
 | 87 | +        assert_biteq!(scalbn(F::ZERO, 10), F::ZERO);  | 
 | 88 | +        assert_biteq!(scalbn(F::ZERO, 0), F::ZERO);  | 
 | 89 | +        assert_biteq!(scalbn(F::ZERO, -10), F::ZERO);  | 
 | 90 | + | 
 | 91 | +        // `scalbn(x, 0)` returns `x`.  | 
 | 92 | +        assert_biteq!(scalbn(F::MIN, 0), F::MIN);  | 
 | 93 | +        assert_biteq!(scalbn(F::MAX, 0), F::MAX);  | 
 | 94 | +        assert_biteq!(scalbn(F::INFINITY, 0), F::INFINITY);  | 
 | 95 | +        assert_biteq!(scalbn(F::NEG_INFINITY, 0), F::NEG_INFINITY);  | 
 | 96 | +        assert_biteq!(scalbn(F::ZERO, 0), F::ZERO);  | 
 | 97 | +        assert_biteq!(scalbn(F::NEG_ZERO, 0), F::NEG_ZERO);  | 
 | 98 | + | 
 | 99 | +        // `scalbn(±∞, n)` returns `±∞`.  | 
 | 100 | +        assert_biteq!(scalbn(F::INFINITY, 10), F::INFINITY);  | 
 | 101 | +        assert_biteq!(scalbn(F::INFINITY, -10), F::INFINITY);  | 
 | 102 | +        assert_biteq!(scalbn(F::NEG_INFINITY, 10), F::NEG_INFINITY);  | 
 | 103 | +        assert_biteq!(scalbn(F::NEG_INFINITY, -10), F::NEG_INFINITY);  | 
 | 104 | + | 
 | 105 | +        // NaN should remain NaNs.  | 
 | 106 | +        assert!(scalbn(F::NAN, 10).is_nan());  | 
 | 107 | +        assert!(scalbn(F::NAN, 0).is_nan());  | 
 | 108 | +        assert!(scalbn(F::NAN, -10).is_nan());  | 
 | 109 | +        assert!(scalbn(-F::NAN, 10).is_nan());  | 
 | 110 | +        assert!(scalbn(-F::NAN, 0).is_nan());  | 
 | 111 | +        assert!(scalbn(-F::NAN, -10).is_nan());  | 
 | 112 | +    }  | 
 | 113 | + | 
 | 114 | +    #[test]  | 
 | 115 | +    fn spec_test_f32() {  | 
 | 116 | +        spec_test::<f32>();  | 
 | 117 | +    }  | 
 | 118 | + | 
 | 119 | +    #[test]  | 
 | 120 | +    fn spec_test_f64() {  | 
 | 121 | +        spec_test::<f64>();  | 
 | 122 | +    }  | 
 | 123 | +}  | 
0 commit comments