use traits::FloatEFT;
#[inline]
pub fn split<T: FloatEFT>(a: T) -> (T, T) {
let tmp = a.clone() * T::split_coef();
let x = tmp.clone() - (tmp - a.clone());
(x.clone(), a - x)
}
#[inline]
pub fn safesplit_branch<T: FloatEFT>(a: T) -> (T, T) {
if a > T::one() {
let t = split(a * T::epsilon());
(t.0 / T::epsilon(), t.1 / T::epsilon())
} else {
split(a)
}
}
#[inline]
pub fn safesplit_straight<T: FloatEFT>(a: T) -> (T, T, T) {
let aa = a.clone() / T::radix();
let err = a - aa.clone() * T::radix();
let step = (((aa.clone() + T::min_positive()) - aa.clone()) / T::min_positive()) /
(T::epsilon() * T::epsilon() * T::epsilon()) + T::epsilon();
let split_shift = split(aa * step.clone());
(split_shift.0 / step.clone(), split_shift.1 / step, err)
}
#[cfg(test)]
mod tests {
extern crate num_traits;
extern crate rand;
use std::f64;
use self::rand::Rng;
use self::num_traits::Float;
use super::*;
#[allow(dead_code)]
fn is_split(a: f64, b: f64) -> bool {
let (a_mant, a_expo, _) = a.integer_decode();
let (b_mant, b_expo, _) = b.integer_decode();
((a_expo - b_expo >= 27) && (((a_mant | b_mant) & 0x7FFFFFFu64) == 0)) |
((a_expo - b_expo == 26) && ((a_mant & 0x7FFFFFFu64) == 0) && (a_mant % 2 == 0) &&
(b_mant == 0x10_0000_0000_0000)) |
((a_expo == b_expo) && (b_expo == -1022) &&
(b_mant <= 2u64.pow(a_mant.trailing_zeros() - 1)))
}
#[test]
fn normal() {
let mut rng = rand::thread_rng();
for _ in 0..10000000 {
let fl = rng.gen_range(2f64.powi(-1022 + 27), 1e+308) *
(1. - 2. * (rng.gen_range(0, 1) as f64));
let s1 = split(fl);
if s1.0.is_nan() {
continue;
}
assert!(s1.0 + s1.1 == fl);
let s2 = safesplit_straight(fl);
let (s2h_prop, s2l_prop) = (s2.0.integer_decode(), s2.1.integer_decode());
assert!((s2h_prop.0 & 0x7FFFFFF) == 0);
assert!((s2l_prop.0 & 0x7FFFFFF) == 0);
assert!(s2.2.abs() <= f64::MIN_POSITIVE * 2f64.powi(-52));
assert!(s2.0.abs() * 2f64.powi(-26) >= s2.1.abs());
assert_eq!(s1.0, s2.0 * 2.);
assert_eq!(s1.1, s2.1 * 2.);
}
}
#[test]
fn large() {
let mut rng = rand::thread_rng();
for _ in 0..10000000 {
let fl = (rng.gen_range::<i64>(-0x1F_FFFF_FFFF_FFFF, 0x20_0000_0000_0000) as f64) *
2f64.powi(1022 - 52);
let s = safesplit_straight(fl);
if s.0.is_nan() {
unreachable!()
}
let (sh_prop, sl_prop) = (s.0.integer_decode(), s.1.integer_decode());
println!("{:e}, {:b}, {:b}", s.0, sh_prop.0, sh_prop.1);
assert!((sh_prop.0 & 0x7FFFFFF) == 0);
assert!((sl_prop.0 & 0x7FFFFFF) == 0);
assert_eq!(((s.2 + s.1 * 2.) + s.0) + s.0, fl);
assert!(s.0.abs() * 2f64.powi(-26) >= s.1.abs());
}
let s = safesplit_straight(f64::MAX);
let (sh_prop, sl_prop) = (s.0.integer_decode(), s.1.integer_decode());
assert!((sh_prop.0 & 0x7FFFFFF) == 0);
assert!((sl_prop.0 & 0x7FFFFFF) == 0);
assert_eq!(((s.2 + s.1 * 2.) + s.0) + s.0, f64::MAX);
assert!(s.0.abs() * 2f64.powi(-26) >= s.1.abs());
}
#[test]
fn subnormal() {
let mut rng = rand::thread_rng();
for _ in 0..10000000 {
let fl = ((rng.gen_range::<i64>(-0xFFFF_FFFF_FFFF, 0x1_0000_0000_0000) as f64) *
2f64.powi(-1022)) * 2f64.powi(-52);
let s = safesplit_straight(fl);
assert_eq!(((s.2 + s.1 * 2.) + s.0) + s.0, fl);
assert!(s.0.abs() * 2f64.powi(-26) >= s.1.abs());
}
}
}