use super::extended_float;
use super::num::*;
use super::powers::*;
#[inline(always)]
pub(crate) fn mul(x: u64, y: u64) -> (u64, u64) {
let x1 = x >> u64::HALF;
let x0 = x & u64::LOMASK;
let y1 = y >> u64::HALF;
let y0 = y & u64::LOMASK;
let w0 = x0 * y0;
let tmp = (x1 * y0) + (w0 >> u64::HALF);
let w1 = tmp & u64::LOMASK;
let w2 = tmp >> u64::HALF;
let w1 = w1 + x0 * y1;
let hi = (x1 * y1) + w2 + (w1 >> u64::HALF);
let lo = x.wrapping_mul(y);
(hi, lo)
}
#[inline(always)]
fn shift_to_carry(x_hi: u64, exp2: i32, carry_shift: i32) -> (u64, i32) {
let msb_shift = u64::FULL - 1;
let msb = x_hi >> msb_shift;
let shift = msb.as_i32() + carry_shift;
let mantissa = x_hi >> shift;
let exp2 = exp2 - (1i32 ^ msb.as_i32());
(mantissa, exp2)
}
#[inline(always)]
fn to_float<F>(mantissa: u64, exp: i32) -> (F, bool)
where
F: Float,
{
if exp <= -(F::MANTISSA_SIZE + 2) {
return (F::ZERO, true);
} else if exp <= 0 {
return (F::ZERO, false);
}
let mut exp = F::Unsigned::as_cast(exp);
let mut mantissa = F::Unsigned::as_cast(mantissa);
let zero = F::Unsigned::ZERO;
let one = F::Unsigned::as_cast(1);
mantissa += mantissa & one;
mantissa >>= 1i32;
let precision = F::MANTISSA_SIZE + 1;
if mantissa >> precision > zero {
mantissa >>= 1i32;
exp += one;
}
let mask = F::MANTISSA_MASK | F::HIDDEN_BIT_MASK;
debug_assert!(mantissa & mask == mantissa);
let max_exp = F::MAX_EXPONENT + F::EXPONENT_BIAS;
if exp >= F::Unsigned::as_cast(max_exp) {
let float = F::from_bits(F::INFINITY_BITS);
return (float, true);
}
mantissa &= F::MANTISSA_MASK;
exp <<= F::MANTISSA_SIZE;
let bits = exp | mantissa;
(F::from_bits(bits), true)
}
#[inline]
pub(super) fn eisel_lemire<F>(mantissa: u64, exponent: i32) -> (F, bool)
where
F: Float,
{
if exponent < MIN_DENORMAL_EXP10 {
return (F::ZERO, true);
} else if exponent > MAX_NORMAL_EXP10 {
let float = F::from_bits(F::INFINITY_BITS);
return (float, true);
}
let ctlz = mantissa.leading_zeros() as i32;
let mantissa = mantissa << ctlz;
let bias = F::EXPONENT_BIAS - F::MANTISSA_SIZE;
let unbiased_exp2 = (217706 * exponent as i64) >> 16;
let exp2 = unbiased_exp2 as i32 + (u64::FULL + bias) - ctlz;
let (exp10_hi, exp10_lo) = POWERS_OF_10[(exponent - MIN_DENORMAL_EXP10) as usize];
let exp10_hi = exp10_hi;
let exp10_lo = exp10_lo;
let (mut x_hi, mut x_lo) = mul(mantissa, exp10_hi);
let carry_size = F::MANTISSA_SIZE + 2;
let carry_shift = u64::FULL - carry_size - 1;
let mask = (1u64 << carry_shift) - 1;
if x_hi & mask == mask && x_lo.wrapping_add(mantissa) < mantissa {
let (y_hi, y_lo) = mul(mantissa, exp10_lo);
let mut merged_hi = x_hi;
let merged_lo = x_lo.wrapping_add(y_hi);
if merged_lo < x_lo {
merged_hi += 1;
}
if merged_hi & mask == mask
&& merged_lo.wrapping_add(1) == 0
&& y_lo.wrapping_add(mantissa) < mantissa
{
return (F::ZERO, false);
} else {
x_hi = merged_hi;
x_lo = merged_lo;
}
}
let (mantissa, exp2) = shift_to_carry(x_hi, exp2, carry_shift);
if x_lo == 0 && x_hi & mask == 0 && mantissa & 3 == 1 {
return (F::ZERO, false);
}
to_float(mantissa, exp2)
}
#[inline]
pub(super) fn moderate_path<F>(mantissa: u64, exponent: i32, truncated: bool) -> (F, bool)
where
F: Float,
{
let (float, valid) = eisel_lemire(mantissa, exponent);
if valid {
if !truncated {
(float, true)
} else {
let mantissa_up = mantissa + 1;
let (float_up, valid) = eisel_lemire(mantissa_up, exponent);
if valid && float == float_up {
(float, true)
} else {
(float, false)
}
}
} else {
extended_float::moderate_path::<F>(mantissa, exponent, truncated)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_halfway_round_down() {
assert_eq!((9007199254740992.0, true), eisel_lemire::<f64>(9007199254740992, 0));
assert_eq!((0.0, false), eisel_lemire::<f64>(9007199254740993, 0));
assert_eq!((9007199254740994.0, true), eisel_lemire::<f64>(9007199254740994, 0));
assert_eq!((9223372036854775808.0, true), eisel_lemire::<f64>(9223372036854775808, 0));
assert_eq!((0.0, false), eisel_lemire::<f64>(9223372036854776832, 0));
assert_eq!((9223372036854777856.0, true), eisel_lemire::<f64>(9223372036854777856, 0));
assert_eq!((0.0, false), eisel_lemire::<f64>(9007199254740992000, -3));
assert_eq!((0.0, false), eisel_lemire::<f64>(9007199254740993000, -3));
assert_eq!((0.0, false), eisel_lemire::<f64>(9007199254740994000, -3));
assert_eq!((9007199254740992.0, true), moderate_path::<f64>(9007199254740992, 0, false));
assert_eq!((9007199254740992.0, false), moderate_path::<f64>(9007199254740993, 0, false));
assert_eq!((9007199254740994.0, true), moderate_path::<f64>(9007199254740994, 0, false));
assert_eq!(
(9223372036854775808.0, true),
moderate_path::<f64>(9223372036854775808, 0, false)
);
assert_eq!(
(9223372036854775808.0, false),
moderate_path::<f64>(9223372036854776832, 0, false)
);
assert_eq!(
(9223372036854777856.0, true),
moderate_path::<f64>(9223372036854777856, 0, false)
);
assert_eq!(
(9007199254740992.0, true),
moderate_path::<f64>(9007199254740992000, -3, false)
);
assert_eq!(
(9007199254740992.0, false),
moderate_path::<f64>(9007199254740993000, -3, false)
);
assert_eq!(
(9007199254740994.0, true),
moderate_path::<f64>(9007199254740994000, -3, false)
);
}
#[test]
fn test_halfway_round_up() {
assert_eq!((9007199254740994.0, true), eisel_lemire::<f64>(9007199254740994, 0));
assert_eq!((9007199254740996.0, true), eisel_lemire::<f64>(9007199254740995, 0));
assert_eq!((9007199254740996.0, true), eisel_lemire::<f64>(9007199254740996, 0));
assert_eq!((18014398509481988.0, true), eisel_lemire::<f64>(18014398509481988, 0));
assert_eq!((18014398509481992.0, true), eisel_lemire::<f64>(18014398509481990, 0));
assert_eq!((18014398509481992.0, true), eisel_lemire::<f64>(18014398509481992, 0));
assert_eq!((9223372036854777856.0, true), eisel_lemire::<f64>(9223372036854777856, 0));
assert_eq!((9223372036854779904.0, true), eisel_lemire::<f64>(9223372036854778880, 0));
assert_eq!((9223372036854779904.0, true), eisel_lemire::<f64>(9223372036854779904, 0));
assert_eq!((0.0, false), eisel_lemire::<f64>(9007199254740994000, -3));
assert_eq!((0.0, false), eisel_lemire::<f64>(9007199254740995000, -3));
assert_eq!((0.0, false), eisel_lemire::<f64>(9007199254740996000, -3));
assert_eq!((9007199254740994.0, true), moderate_path::<f64>(9007199254740994, 0, false));
assert_eq!((9007199254740996.0, true), moderate_path::<f64>(9007199254740995, 0, false));
assert_eq!((9007199254740996.0, true), moderate_path::<f64>(9007199254740996, 0, false));
assert_eq!((18014398509481988.0, true), moderate_path::<f64>(18014398509481988, 0, false));
assert_eq!((18014398509481992.0, true), moderate_path::<f64>(18014398509481990, 0, false));
assert_eq!((18014398509481992.0, true), moderate_path::<f64>(18014398509481992, 0, false));
assert_eq!(
(9223372036854777856.0, true),
moderate_path::<f64>(9223372036854777856, 0, false)
);
assert_eq!(
(9223372036854779904.0, true),
moderate_path::<f64>(9223372036854778880, 0, false)
);
assert_eq!(
(9223372036854779904.0, true),
moderate_path::<f64>(9223372036854779904, 0, false)
);
assert_eq!(
(9007199254740994.0, true),
moderate_path::<f64>(9007199254740994000, -3, false)
);
assert_eq!(
(9007199254740994.0, false),
moderate_path::<f64>(9007199254740995000, -3, false)
);
assert_eq!(
(9007199254740996.0, true),
moderate_path::<f64>(9007199254740996000, -3, false)
);
}
#[test]
fn test_mul() {
let e1 = 11529215046068469760; let e10 = 10737418240000000000; assert_eq!((0x5D21DBA000000000, 0x0000000000000000), mul(e1, e10));
let e9 = 17179869184000000000; let e70 = 13363823550460978230; assert_eq!((0xACB92ED9397BF995, 0xA23A700000000000), mul(e9, e70));
let e280 = 10162340898095201970; assert_eq!((0x83585D8FD9C25DB6, 0xFC31D00000000000), mul(e9, e280));
let e0 = 9223372036854775808; let e290 = 11830521861667747109; assert_eq!((0x52173A79E8197A92, 0x8000000000000000), mul(e0, e290));
}
}