|
| 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