fast_posit/
lib.rs

1#![cfg_attr(not(test), no_std)]
2
3//! This crate provides a correct, clean, flexible, and 🚀 *fast* software implementation of
4//! [Posit arithmetic](https://posithub.org/docs/posit_standard-2.pdf).
5//!
6//! # Introduction
7//!
8//! Posits are an alternative floating point format proposed by John Gustafson in 2017, with the
9//! first published standard in 2022. They have several interesting features that make them an
10//! excellent replacement for traditional IEEE754 floats, in domains such as neural networks or
11//! HPC.
12//!
13//! The following references are useful if you are not yet familiar with posits:
14//!
15//!   - [Posit standard](https://posithub.org/docs/posit_standard-2.pdf) (2022)
16//!   - [Original extended paper](https://posithub.org/docs/Posits4.pdf) (2017)
17//!   - [Book](https://doi.org/10.1201/9781003466024 ) (2024)
18//!
19//! This crate aims to implement the entire posit standard and beyond, including features such as
20//! arbitrary posit and quire sizes beyond those prescribed by the standard. Versions prior to
21//! 1.0.0, however, may be incomplete. Correctness is ensured via extensive testing against an
22//! oracle.
23//!
24//! # Usage
25//!
26//! The following is an extended tour over the main functionality of the crate, sort of in the
27//! style of "learn X in Y minutes". For more information, refer to the documentation of specific
28//! types and functions.
29//!
30//! ```
31//! // Use standard posit types, or define your own.
32//! # use fast_posit::Posit;
33//! use fast_posit::{p8, p16, p32, p64};  // Standard: n bits, 2 exponent bits
34//! type MyPosit = Posit<24, 3, i32>;  // Non-standard: 24 bits, 3 exponent bits
35//!
36//! // Create posits from ints, IEEE floats, strings, constants, or a raw bit representation.
37//! # use fast_posit::{RoundFrom, RoundInto};
38//! let a = p32::round_from(2.71_f64);
39//! let b = p32::round_from(42_i32);
40//! let c = p32::from_bits(0x7f001337);
41//! let d = p32::MIN_POSITIVE;
42//!
43//! // Perform basic arithmetic and comparisons using the usual operators.
44//! assert!(p16::round_from(2.14) + p16::ONE == p16::round_from(3.14));
45//! assert!(p16::MIN_POSITIVE < 1e-15.round_into());
46//!
47//! // Convert posits back to ints, IEEE floats, strings, or a raw bit representation.
48//! assert_eq!(p8::ONE.to_bits(), 0b01000000);
49//!
50//! // Use a quire to calculate sums and dot products _without loss of precision_!
51//! use fast_posit::{q8, q16, q32, q64};
52//! let mut quire = q16::ZERO;  
53//! quire += p16::MAX;
54//! quire += p16::round_from(0.001); // Would overflow
55//! quire -= p16::MAX;
56//! //TODO let result = p16::from(quire);
57//! // Correct result with the quire, no issues with rounding errors.
58//! //TODO assert_eq!(result, p16::round_from(0.001))
59//! // The same sum without the quire would give a wrong result, due to double rounding.
60//! let posit = p16::MAX + p16::round_from(0.001) - p16::MAX;
61//! assert_eq!(posit, p16::ZERO);
62//!
63//! // Use a quire per thread to ensure the result is the same regardless of parallelisation!
64//! let mut quires = [q16::ZERO; 8];
65//! for thread in 0..8 {
66//!   let local_quire = &mut quires[thread];
67//!   *local_quire += p16::round_from(123);
68//!   *local_quire += p16::round_from(456);
69//!   // ...
70//! }
71//! // Assemble the final result by summing the thread-local quires first, then converting to posit.
72//! //TODO for thread in 1..8 {
73//! //TODO   quires[0] += quire[thread]
74//! //TODO }
75//! //TODO let result: p16 = quires[0].round_into();
76//!
77//! // Use mixed-precision with no hassle; it's very cheap when the ES is the same.
78//! //TODO let mut quire = q64::ZERO;
79//! //TODO quire += q64::from(42);
80//! //TODO quire += q8::round_from(12).into();
81//! ```
82//!
83//! # Performance
84//!
85//! In terms of performance, you can expect as a *very rough estimate* 50 to 250 Mops/s
86//! (corresponding to about a 4–20× slowdown relative to native hw FPU operations) on an 11th gen
87//! Intel x86 core at 2.80GHz. This is, as far as we're aware, faster (or at least as fast) than
88//! any freely available software implementation of posit arithmetic.
89//!
90//! Needless to say, both absolute performance and relative performance vs the FPU will vary
91//! depending on your system.
92//!
93//! This crate includes benchmarks; run them with `cargo bench -F bench`.
94
95mod posit;
96mod underlying;
97
98pub use posit::Posit;
99pub use posit::quire::Quire;
100pub use underlying::Int;
101pub use posit::convert::{RoundFrom, RoundInto};
102
103/// Standard-defined 8-bit posit (with 2-bit exponent).
104#[allow(non_camel_case_types)]
105pub type p8 = Posit<8, 2, i8>;
106
107/// Standard-defined 16-bit posit (with 2-bit exponent).
108#[allow(non_camel_case_types)]
109pub type p16 = Posit<16, 2, i16>;
110
111/// Standard-defined 32-bit posit (with 2-bit exponent).
112#[allow(non_camel_case_types)]
113pub type p32 = Posit<32, 2, i32>;
114
115/// Standard-defined 64-bit posit (with 2-bit exponent).
116#[allow(non_camel_case_types)]
117pub type p64 = Posit<64, 2, i64>;
118
119/// Standard-defined 128-bit quire for a [p8].
120#[allow(non_camel_case_types)]
121pub type q8 = Quire<8, 2, 16>;
122
123/// Standard-defined 256-bit quire for a [p16].
124#[allow(non_camel_case_types)]
125pub type q16 = Quire<16, 2, 32>;
126
127/// Standard-defined 512-bit quire for a [p32].
128#[allow(non_camel_case_types)]
129pub type q32 = Quire<32, 2, 64>;
130
131/// Standard-defined 1024-bit quire for a [p64].
132#[allow(non_camel_case_types)]
133pub type q64 = Quire<64, 2, 128>;
134
135/// Re-export some internals for benchmarking purposes, only on `feature = "bench"`.
136#[cfg(feature = "bench")]
137mod bench;