use traits::FloatEFT;
#[cfg(any(feature = "use-fma", feature = "doc"))]
use fma::{fma, Fma};
#[inline]
pub fn fasttwosum<T: FloatEFT>(x: T, y: T) -> (T, T) {
let sum = x.clone() + y.clone();
(sum.clone(), y - (sum - x))
}
#[inline]
pub fn twosum<T: FloatEFT>(x: T, y: T) -> (T, T) {
let sum = x.clone() + y.clone();
let tmp = sum.clone() - x.clone();
(sum.clone(), (x - (sum.clone() - tmp.clone())) + (y - tmp))
}
#[inline]
pub fn safetwosum_branch<T: FloatEFT>(x: T, y: T) -> (T, T) {
if x.clone().abs() > y.clone().abs() {
fasttwosum(x, y)
} else {
fasttwosum(y, x)
}
}
#[inline]
pub fn safetwosum_straight<T: FloatEFT>(x: T, y: T) -> (T, T) {
let s = x.clone() + y.clone();
let (xx, yy) = (x.clone() / T::radix(), y.clone() / T::radix()); let err_uf = (x - xx.clone() * T::radix()) + (y - yy.clone() * T::radix()); let (ss, ee) = twosum(xx, yy); let err_h = T::radix() * ss - s.clone(); (s, (T::radix() * ee + err_h) + err_uf)
}
#[cfg(any(feature = "use-fma", feature = "doc"))]
#[inline]
pub fn safetwosum_fma<T: FloatEFT + Fma>(x: T, y: T) -> (T, T) {
let s = x.clone() + y.clone();
let (xx, yy) = (x.clone() / T::radix(), y.clone() / T::radix()); let err_uf = fma(-T::radix(), xx.clone(), x) + fma(-T::radix(), yy.clone(), y);
let (ss, ee) = twosum(xx, yy); let err_h = fma(T::radix(), ss, -s.clone()); (s, fma(T::radix(), ee, err_h) + err_uf)
}
#[cfg(test)]
mod tests {
extern crate rand;
use std::f64;
use self::rand::Rng;
use super::*;
#[test]
fn normal() {
let mut rng = rand::thread_rng();
for _ in 0..100000000 {
let (l, r) = (rng.gen::<f64>(), rng.gen::<f64>());
let (a1, b1) = safetwosum_branch(l, r);
let (a2, b2) = safetwosum_straight(l, r);
assert!(((a1 == a2) && (b1 == b2)) || ((a1 == a2) && a1.is_infinite()));
#[cfg(feature = "use-fma")]
{
let (a2, b2) = safetwosum_fma(l, r);
assert!(((a1 == a2) && (b1 == b2)) || ((a1 == a2) && a1.is_infinite()));
}
}
}
#[test]
fn large() {
let mut rng = rand::thread_rng();
for _ in 0..10000000 {
let (l, r) =
(rng.gen_range::<f64>(2f64.powi(1020), f64::MAX) * rng.choose(&[1., -1.]).unwrap(),
rng.gen_range::<f64>(2f64.powi(1020), f64::MAX) * rng.choose(&[1., -1.]).unwrap());
let (a1, b1) = safetwosum_branch(l, r);
let (a2, b2) = safetwosum_straight(l, r);
assert!(((a1 == a2) && (b1 == b2)) || ((a1 == a2) && a1.is_infinite()));
#[cfg(feature = "use-fma")]
{
let (a2, b2) = safetwosum_fma(l, r);
assert!(((a1 == a2) && (b1 == b2)) || ((a1 == a2) && a1.is_infinite()));
}
}
}
#[test]
fn subnormal() {
let mut rng = rand::thread_rng();
for _ in 0..10000000 {
let (l, r) = ((rng.gen_range::<i64>(-0x1F_FFFF_FFFF_FFFF, 0x20_0000_0000_0000) as
f64) * 2f64.powi(-1022) * 2f64.powi(-52),
rng.gen::<f64>());
let (a1, b1) = safetwosum_branch(l, r);
let (a2, b2) = safetwosum_straight(l, r);
assert!((a1 == a2) && (b1 == b2));
#[cfg(feature = "use-fma")]
{
let (a2, b2) = safetwosum_fma(l, r);
assert!((a1 == a2) && (b1 == b2));
}
}
}
#[test]
fn corner_case() {
let res1 = safetwosum_straight(3.5630624444874539e+307, -1.7976931348623157e+308);
assert!(!(res1.1 as f64).is_nan());
#[cfg(feature = "use-fma")]
{
let res2 = safetwosum_fma(3.5630624444874539e+307, -1.7976931348623157e+308);
assert!(res1 == res2);
}
}
}