#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![allow(uncommon_codepoints, mixed_script_confusables)]
use num_traits::float::Float;
use std::iter::Sum;
use std::ops::{Add, AddAssign, Sub, SubAssign};
pub fn two_sum<T: Float>(a: T, b: T) -> (T, T) {
let s = a + b;
let aʹ = s - b;
let bʹ = s - a;
let δa = a - aʹ;
let δb = b - bʹ;
let t = δa + δb;
(s, t)
}
fn two_sub<T: Float>(a: T, b: T) -> (T, T) {
let s = a - b;
let aʹ = s + b;
let bʹ = a - s;
let δa = a - aʹ;
let δb = b - bʹ;
let t = δa - δb;
(s, t)
}
pub fn fast_two_sum<T: Float>(a: T, b: T) -> (T, T) {
let s = a + b;
let bʹ = s - a;
let δb = b - bʹ;
(s, δb)
}
#[derive(Clone, Debug, PartialEq)]
pub struct KahanBabuska<T> {
pub sum: T,
pub comp: T,
}
impl<T: Float> KahanBabuska<T> {
pub fn new() -> Self {
Self {
sum: T::zero(),
comp: T::zero(),
}
}
pub fn total(&self) -> T {
self.sum + self.comp
}
}
impl<T: Float> Add<T> for KahanBabuska<T> {
type Output = Self;
fn add(mut self, rhs: T) -> Self::Output {
self += rhs;
self
}
}
impl<T: Float> AddAssign<T> for KahanBabuska<T> {
fn add_assign(&mut self, rhs: T) {
let (s, c) = fast_two_sum(self.sum, rhs + self.comp);
self.sum = s;
self.comp = c;
}
}
impl<T: Float> Sub<T> for KahanBabuska<T> {
type Output = Self;
fn sub(mut self, rhs: T) -> Self::Output {
self -= rhs;
self
}
}
impl<T: Float> SubAssign<T> for KahanBabuska<T> {
fn sub_assign(&mut self, rhs: T) {
let (s, c) = fast_two_sum(self.sum, self.comp - rhs);
self.sum = s;
self.comp = c;
}
}
impl<T: Float> Add<&T> for KahanBabuska<T> {
type Output = Self;
fn add(self, rhs: &T) -> Self::Output {
self + *rhs
}
}
impl<T: Float> AddAssign<&T> for KahanBabuska<T> {
fn add_assign(&mut self, rhs: &T) {
*self += *rhs;
}
}
impl<T: Float> Sub<&T> for KahanBabuska<T> {
type Output = Self;
fn sub(self, rhs: &T) -> Self::Output {
self - *rhs
}
}
impl<T: Float> SubAssign<&T> for KahanBabuska<T> {
fn sub_assign(&mut self, rhs: &T) {
*self -= *rhs;
}
}
impl<T: Float, V> Sum<V> for KahanBabuska<T>
where
Self: Add<V, Output = Self>,
{
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = V>,
{
iter.fold(KahanBabuska::new(), |k, item| k + item)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct KahanBabuskaNeumaier<T> {
pub sum: T,
pub comp: T,
}
impl<T: Float> KahanBabuskaNeumaier<T> {
pub fn new() -> Self {
Self {
sum: T::zero(),
comp: T::zero(),
}
}
pub fn total(&self) -> T {
self.sum + self.comp
}
}
impl<T: Float> Add<T> for KahanBabuskaNeumaier<T> {
type Output = Self;
fn add(mut self, rhs: T) -> Self::Output {
self += rhs;
self
}
}
impl<T: Float> AddAssign<T> for KahanBabuskaNeumaier<T> {
fn add_assign(&mut self, rhs: T) {
let (s, c) = two_sum(self.sum, rhs);
self.sum = s;
self.comp = self.comp + c;
}
}
impl<T: Float> Sub<T> for KahanBabuskaNeumaier<T> {
type Output = Self;
fn sub(mut self, rhs: T) -> Self::Output {
self -= rhs;
self
}
}
impl<T: Float> SubAssign<T> for KahanBabuskaNeumaier<T> {
fn sub_assign(&mut self, rhs: T) {
let (s, c) = two_sub(self.sum, rhs);
self.sum = s;
self.comp = self.comp + c;
}
}
impl<T: Float> Add<&T> for KahanBabuskaNeumaier<T> {
type Output = Self;
fn add(self, rhs: &T) -> Self::Output {
self + *rhs
}
}
impl<T: Float> AddAssign<&T> for KahanBabuskaNeumaier<T> {
fn add_assign(&mut self, rhs: &T) {
*self += *rhs;
}
}
impl<T: Float> Sub<&T> for KahanBabuskaNeumaier<T> {
type Output = Self;
fn sub(self, rhs: &T) -> Self::Output {
self - *rhs
}
}
impl<T: Float> SubAssign<&T> for KahanBabuskaNeumaier<T> {
fn sub_assign(&mut self, rhs: &T) {
*self -= *rhs;
}
}
impl<T: Float, V> Sum<V> for KahanBabuskaNeumaier<T>
where
Self: Add<V, Output = Self>,
{
fn sum<I>(iter: I) -> Self
where
I: Iterator<Item = V>,
{
iter.fold(KahanBabuskaNeumaier::new(), |k, item| k + item)
}
}
pub type KahanBabuška<T> = KahanBabuska<T>;
pub type KahanBabuškaNeumaier<T> = KahanBabuskaNeumaier<T>;
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn test_two_sub() {
assert_eq!(two_sub(0.1, 0.2), two_sum(0.1, -0.2));
}
#[test]
fn kahan_123() {
let mut k = KahanBabuska::new();
let mut s = 0.0;
k += 0.1;
s += 0.1;
k += 0.2;
s += 0.2;
k += -0.3;
s += -0.3;
assert_eq!(k.sum, 0.0);
assert_eq!(k.comp, 0.0);
assert_eq!(s, f64::EPSILON / 4.);
}
#[test]
fn kahan_tenth() {
let mut k = KahanBabuska::new();
let mut s = 0.0;
for _ in 0..10 {
k += 0.1;
s += 0.1;
}
k += -1.0;
s += -1.0;
assert_eq!(k.sum, 0.0);
assert_eq!(k.comp, 0.0);
assert_eq!(s, -f64::EPSILON / 2.);
}
#[test]
fn kahan_123_iter() {
assert_eq!(
[0.1, 0.2, -0.3].iter().sum::<KahanBabuska<f64>>().total(),
0.0
);
assert_eq!(
[0.1, 0.2, -0.3]
.iter()
.cloned()
.sum::<KahanBabuska<f64>>()
.total(),
0.0
);
}
#[test]
fn kahan_tenth_iter() {
assert_eq!([0.1; 10].iter().sum::<KahanBabuska<f64>>().total(), 1.0);
assert_eq!(
[0.1; 10].iter().cloned().sum::<KahanBabuska<f64>>().total(),
1.0
);
}
#[test]
fn kahan_large() {
assert_eq!(
[1.0, 1e100, 1.0, -1e100]
.iter()
.sum::<KahanBabuska<f64>>()
.total(),
0.0
);
}
#[test]
fn neumaier_large() {
assert_eq!(
[1.0, 1e100, 1.0, -1e100]
.iter()
.sum::<KahanBabuskaNeumaier<f64>>()
.total(),
2.0
);
}
}