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