use super::float::*;
use super::num::*;
use super::powers::*;
use super::rounding::*;
#[inline]
fn nearest_error_is_accurate(errors: u64, fp: &ExtendedFloat, extrabits: u64) -> bool {
if extrabits == 65 {
!fp.mant.overflowing_add(errors).1
} else {
let mask: u64 = lower_n_mask(extrabits);
let extra: u64 = fp.mant & mask;
let halfway: u64 = lower_n_halfway(extrabits);
let cmp1 = halfway.wrapping_sub(errors) < extra;
let cmp2 = extra < halfway.wrapping_add(errors);
!(cmp1 && cmp2)
}
}
#[inline(always)]
fn error_scale() -> u32 {
8
}
#[inline(always)]
fn error_halfscale() -> u32 {
error_scale() / 2
}
#[inline]
fn error_is_accurate<F: Float>(count: u32, fp: &ExtendedFloat) -> bool {
let bias = -(F::EXPONENT_BIAS - F::MANTISSA_SIZE);
let denormal_exp = bias - 63;
let extrabits = match fp.exp <= denormal_exp {
true => 64 - F::MANTISSA_SIZE + denormal_exp - fp.exp,
false => 63 - F::MANTISSA_SIZE,
};
let extrabits = extrabits as u64;
let errors = count as u64;
if extrabits > 65 {
return true;
}
nearest_error_is_accurate(errors, fp, extrabits)
}
fn multiply_exponent_extended<F>(fp: &mut ExtendedFloat, exponent: i32, truncated: bool) -> bool
where
F: Float,
{
if exponent < MIN_DENORMAL_EXP10 {
fp.mant = 0;
true
} else if exponent > MAX_NORMAL_EXP10 {
fp.mant = 1 << 63;
fp.exp = 0x7FF;
true
} else {
let mut errors: u32 = 0;
if truncated {
errors += error_halfscale();
}
let exp = -63 + (217706 * exponent as i64 >> 16);
let mant = POWERS_OF_10[(exponent - MIN_DENORMAL_EXP10) as usize].0;
let large = ExtendedFloat { mant, exp: exp as i32 };
fp.normalize();
fp.imul(&large);
if errors > 0 {
errors += 1;
}
errors += error_halfscale();
let shift = fp.normalize();
errors <<= shift;
error_is_accurate::<F>(errors, &fp)
}
}
#[inline]
pub(super) fn moderate_path<F>(mantissa: u64, exponent: i32, truncated: bool) -> (F, bool)
where
F: Float,
{
let mut fp = ExtendedFloat {
mant: mantissa,
exp: 0,
};
let valid = multiply_exponent_extended::<F>(&mut fp, exponent, truncated);
if valid {
let float = fp.into_float::<F>();
(float, true)
} else {
let float = fp.into_downward_float::<F>();
(float, false)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn moderate_path_test() {
let (f, valid) = moderate_path::<f64>(1234567890, -1, false);
assert!(valid, "should be valid");
assert_eq!(f, 123456789.0);
let (f, valid) = moderate_path::<f64>(1234567891, -1, false);
assert!(valid, "should be valid");
assert_eq!(f, 123456789.1);
let (f, valid) = moderate_path::<f64>(12345678912, -2, false);
assert!(valid, "should be valid");
assert_eq!(f, 123456789.12);
let (f, valid) = moderate_path::<f64>(123456789123, -3, false);
assert!(valid, "should be valid");
assert_eq!(f, 123456789.123);
let (f, valid) = moderate_path::<f64>(1234567891234, -4, false);
assert!(valid, "should be valid");
assert_eq!(f, 123456789.1234);
let (f, valid) = moderate_path::<f64>(12345678912345, -5, false);
assert!(valid, "should be valid");
assert_eq!(f, 123456789.12345);
let (f, valid) = moderate_path::<f64>(123456789123456, -6, false);
assert!(valid, "should be valid");
assert_eq!(f, 123456789.123456);
let (f, valid) = moderate_path::<f64>(1234567891234567, -7, false);
assert!(valid, "should be valid");
assert_eq!(f, 123456789.1234567);
let (f, valid) = moderate_path::<f64>(12345678912345679, -8, false);
assert!(valid, "should be valid");
assert_eq!(f, 123456789.12345679);
let (f, valid) = moderate_path::<f64>(4628372940652459, -17, false);
assert!(valid, "should be valid");
assert_eq!(f, 0.04628372940652459);
let (f, valid) = moderate_path::<f64>(26383446160308229, -272, false);
assert!(valid, "should be valid");
assert_eq!(f, 2.6383446160308229e-256);
let (_, valid) = moderate_path::<f64>(26383446160308230, -272, false);
assert!(!valid, "should be invalid");
}
}