Crate accurate

source ·
Expand description

A collection of (more or less) accurate floating point algorithms

This crate implements several algorithms for floating point summation and dot product. The algorithms are realized as types that implement the SumAccumulator and DotAccumulator trait.

§Basic usage

Calculating a sum (or a dot product) begins by initializing an accumulator to zero:

use accurate::traits::*; // Most functionality is derived from traits in this module
use accurate::sum::NaiveSum; // Chose a specific algorithm to perform summation / dot product

let s = NaiveSum::<f32>::zero();

The accumulator traits are generic over the type of the underlying floating point numbers and the zero() constructor is supported if the number type implements the Zero trait. Alternatively the accumulator traits imply that an accumulator can be constructed from() an arbitrary value of the number type.

let s = NaiveSum::from(42.0f64);

The actual calculation is performed via the Add<F, Output = Self> trait that is also implied by the SumAccumulator trait, where F is the type of the floating point numbers.

use accurate::sum::Sum2;

let s = Sum2::zero() + 1.0f64 + 2.0 + 3.0;

For dot products, the DotAccumulator trait implies Add<(F, F), Output = Self> to allow accumulation of the products of pairs into the final result.

use accurate::dot::NaiveDot;

let d = NaiveDot::zero() + (1.0f64, 1.0f64) + (2.0, 2.0) + (3.0, 3.0);

Once all of the terms have been accumulated, the result can be evaluated using the sum() and dot() methods respectively.

let s = Sum2::zero() + 1.0f64 + 2.0 + 3.0;
assert_eq!(6.0, s.sum());

let d = NaiveDot::zero() + (1.0f64, 1.0f64) + (2.0, 2.0) + (3.0, 3.0);
assert_eq!(14.0, d.dot());

Both sum() and dot() take their argument by value, because the evaluation of the final result is in some cases a destructive operation on the internal state of the accumulator. However, the evaluation of partial results is supported by clone()ing the accumulator.

let s = Sum2::zero() + 1.0f32 + 2.0;
assert_eq!(3.0, s.clone().sum());
let s = s + 3.0;
assert_eq!(6.0, s.sum());

§Iterator consumption

Accumulators can be used in fold() operations on iterators as one would expect.

use accurate::dot::Dot2;

let s = vec![1.0f32, 2.0, 3.0].into_iter().fold(Sum2::zero(), |acc, x| acc + x);
assert_eq!(6.0, s.sum());

let d = vec![1.0f32, 2.0, 3.0].into_iter()
    .zip(vec![1.0, 2.0, 3.0].into_iter())
    .fold(Dot2::zero(), |acc, xy| acc + xy);
assert_eq!(14.0, d.dot());

For convenience, the accumulator traits also define absorb() methods to absorb values from anything that implements IntoIterator.


let s = Sum2::zero().absorb(vec![1.0f32, 2.0, 3.0]);
assert_eq!(6.0, s.sum());

let d = Dot2::zero().absorb(vec![(1.0f32, 1.0), (2.0, 2.0), (3.0, 3.0)]);
assert_eq!(14.0, d.dot());

And for even more convenience, suitable iterators are extended by a sum_with_accumulator() (and dot_with_accumulator()) method that directly evaluates to the result in the floating point number type.


let s = Sum2::zero().absorb(vec![1.0f32, 2.0, 3.0]);
assert_eq!(6.0f64, vec![1.0, 2.0, 3.0].into_iter().sum_with_accumulator::<Sum2<_>>());

assert_eq!(14.0f64, vec![(1.0, 1.0), (2.0, 2.0), (3.0, 3.0)].into_iter()
    .dot_with_accumulator::<Dot2<_>>());

§Parallel computation

If compiled with the parallel feature enabled (which is the default) the rayon parallel iterator facilities are used to perform large calculations in parallel. Parallel calculations are performed through the parallel_sum_with_accumulator() and parallel_dot_with_accumulator() extension methods on parallel iterators.

use rayon::prelude::*;

let xs = vec![1.0f64; 100_000];
let s = xs.par_iter().map(|&x| x).parallel_sum_with_accumulator::<Sum2<_>>();
assert_eq!(100_000.0, s);

Modules§

  • Algorithms for dot product
  • Algorithms for summation
  • Includes all traits of this crate
  • Common infrastructure