fast_posit/posit/quire/
mod.rs

1use super::*;
2
3/// A *quire*, for a posit type with `N` bits and `ES` exponent bits, which is `SIZE` bytes long.
4///
5/// A quire is a fixed-point accumulator that enables sums and dot products of posits to be
6/// calculated with **no** intermediate rounding whatsoever. This has tremendous practical uses,
7/// from solving systems of equations to evaluating neural networks.
8///
9/// The `SIZE` is bounded from below based on the minimum size necessary to hold the product of two
10/// posits. Above this, the more extra space, the more terms can be accumulated without the risk
11/// of overflow (in practice the standard suggests ≈30 extra bits, corresponding to over a billion
12/// terms). It is also required to be a multiple of 8 (64 bits) for performance reasons
13/// (this requirement will be relaxed in the future).
14///
15/// If the quire `SIZE` is smaller than [the minimum size](Quire::MIN_SIZE) necessary for an `N`
16/// bit posit with `ES` exponent bits, or if that size is not a multiple of 8, a **compilation
17/// error** is raised.
18///
19/// Type aliases are provided at the [crate root](crate#types) for the quire types defined in
20/// [the standard](https://posithub.org/docs/posit_standard-2.pdf#subsection.3.1).
21///
22/// # Example
23///
24/// ```
25/// # use fast_posit::{p16, q16, Quire, RoundFrom, RoundInto};
26/// // A 256-bit (32-byte) quire for a posit with 16 bits and 1 exponent bit
27/// type Foo = Quire<16, 1, 32>;
28///
29/// // Compute sums and products in the quire with *no* loss of precision.
30/// let mut quire = q16::ZERO;
31/// quire += p16::MAX;
32/// quire += p16::round_from(0.1);
33/// quire -= p16::MAX;
34/// let result: p16 = (&quire).round_into();  // Only the final step rounds
35///
36/// // Correct result with the quire, no issues with rounding errors.
37/// assert_eq!(result, p16::round_from(0.1));
38///
39/// // The same sum without the quire would give a wrong result, due to double rounding.
40/// let posit = (p16::MAX + p16::round_from(0.1)) - p16::MAX;
41/// assert_eq!(posit, p16::ZERO);
42/// ```
43//
44// The quire is represented as an array of bytes in big-endian order. This is because (we theorise
45// at the moment, with no data) since most operations need to start by checking if the most
46// significant byte is NaR (= 0b10000000), placing that byte first is the best layout for cache.
47//
48// On the other hand, adding with carry is more natural to do in little-endian order, and also
49// avoids the work of shuffling bits from little-endian to big-endian in little-endian
50// architectures... Need to try and profile.
51//
52// It is also aligned to 128 bits, and we restrict `SIZE` to be a multiple of 64-bits (8 bytes).
53#[derive(Clone, Hash)]
54#[repr(align(16))]
55pub struct Quire<
56  const N: u32,
57  const ES: u32,
58  const SIZE: usize,
59> (pub(crate) [u8; SIZE]);
60
61/// Basic constants and functions, such as the position of the fixed point, compile-time checks
62/// that `SIZE` is ≥ the minimum size, etc.
63mod basics;
64
65/// Here is the core algorithm of the quire: adding the product of two posits, as a fixed-point
66/// number, to the quire.
67mod add;
68
69/// The user-facing functions live here: `+=`, `-=`, `add_prod`, `sub_prod`.
70mod ops;
71
72/// [Quire] -> [Posit] and [Posit] -> [Quire].
73mod convert;