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//! Wherever a function corresponds to a function in
31//! [the standard](https://posithub.org/docs/posit_standard-2.pdf), it will be marked accordingly
32//! in its documentation.
33//!
34//! ```
35//! // Use standard posit types, or define your own.
36//! # use fast_posit::Posit;
37//! use fast_posit::{p8, p16, p32, p64};  // Standard: n bits, 2 exponent bits
38//! type MyPosit = Posit<24, 3, i32>;  // Non-standard: 24 bits, 3 exponent bits
39//!
40//! // Create posits from ints, IEEE floats, strings, constants, or a raw bit representation.
41//! use fast_posit::{RoundFrom, RoundInto};
42//! let a = p32::round_from(2.71_f64);
43//! let b = p32::round_from(42_i32);
44//! let c = p32::from_bits(0x7f001337);
45//! let d = p32::MIN_POSITIVE;
46//!
47//! // Perform basic arithmetic and comparisons using the usual operators.
48//! assert!(p16::round_from(2.14) + p16::ONE == p16::round_from(3.14));
49//! assert!(p16::MIN_POSITIVE < 1e-15.round_into());
50//!
51//! // Convert posits back to ints, IEEE floats, strings, or a raw bit representation.
52//! assert_eq!(p8::ONE.to_bits(), 0b01000000);
53//!
54//! // Use a quire to calculate sums and dot products _without loss of precision_!
55//! use fast_posit::{q8, q16, q32, q64};
56//! let mut quire = q16::ZERO;
57//! quire += p16::MAX;
58//! quire += p16::round_from(0.1);
59//! quire -= p16::MAX;
60//! let result: p16 = (&quire).round_into();
61//! // Correct result with the quire, no issues with rounding errors.
62//! assert_eq!(result, p16::round_from(0.1));
63//! // The same sum without the quire would give a wrong result, due to double rounding.
64//! let posit = p16::MAX + p16::round_from(0.1) - p16::MAX;
65//! assert_eq!(posit, p16::ZERO);
66//!
67//! // Use a quire per thread to ensure the result is the same regardless of parallelisation!
68//! let mut quires = [q16::ZERO; 8];
69//! for thread in 0..8 {
70//!   let local_quire = &mut quires[thread];
71//!   *local_quire += p16::round_from(123);
72//!   *local_quire += p16::round_from(456);
73//!   // ...
74//! }
75//! // Assemble the final result by summing the thread-local quires first, then converting to posit.
76//! //TODO for thread in 1..8 {
77//! //TODO   quires[0] += quire[thread]
78//! //TODO }
79//! let result: p16 = (&quires[0]).round_into();
80//!
81//! // Use mixed-precision with no hassle; it's very cheap when the ES is the same.
82//! let terms = [3, 7, 15, 1].map(p8::round_from);
83//! let pi = {
84//!   let mut partial = p64::ZERO;
85//!   for i in terms[1..].iter().rev() {
86//!     partial = p64::ONE / (i.convert() + partial)
87//!   }
88//!   terms[0].convert() + partial
89//! };
90//! assert!((3.141592.round_into() .. 3.141593.round_into()).contains(&pi));
91//! ```
92//!
93//! # Performance
94//!
95//! In terms of performance, you can expect as a *very rough estimate* 50 to 250 Mops/s
96//! (corresponding to about a 4–20× slowdown relative to native hw FPU operations) on an 11th gen
97//! Intel x86 core at 2.80GHz. This is, as far as we're aware, faster (or at least as fast) than
98//! any freely available software implementation of posit arithmetic.
99//!
100//! Needless to say, both absolute performance and relative performance vs the FPU will vary
101//! depending on your system.
102//!
103//! This crate includes benchmarks; run them with `cargo bench -F bench`.
104
105mod posit;
106mod underlying;
107
108pub use posit::Posit;
109pub use posit::quire::Quire;
110pub use underlying::Int;
111pub use posit::convert::{RoundFrom, RoundInto};
112
113/// Standard-defined 8-bit posit (with 2-bit exponent).
114#[allow(non_camel_case_types)]
115pub type p8 = Posit<8, 2, i8>;
116
117/// Standard-defined 16-bit posit (with 2-bit exponent).
118#[allow(non_camel_case_types)]
119pub type p16 = Posit<16, 2, i16>;
120
121/// Standard-defined 32-bit posit (with 2-bit exponent).
122#[allow(non_camel_case_types)]
123pub type p32 = Posit<32, 2, i32>;
124
125/// Standard-defined 64-bit posit (with 2-bit exponent).
126#[allow(non_camel_case_types)]
127pub type p64 = Posit<64, 2, i64>;
128
129/// Standard-defined 128-bit quire for a [p8].
130#[allow(non_camel_case_types)]
131pub type q8 = Quire<8, 2, 16>;
132
133/// Standard-defined 256-bit quire for a [p16].
134#[allow(non_camel_case_types)]
135pub type q16 = Quire<16, 2, 32>;
136
137/// Standard-defined 512-bit quire for a [p32].
138#[allow(non_camel_case_types)]
139pub type q32 = Quire<32, 2, 64>;
140
141/// Standard-defined 1024-bit quire for a [p64].
142#[allow(non_camel_case_types)]
143pub type q64 = Quire<64, 2, 128>;
144
145/// Re-export some internals for benchmarking purposes, only on `feature = "bench"`.
146#[cfg(feature = "bench")]
147mod bench;
148
149/// Set the number of proptest cases globally.
150///
151/// This number strikes a balance between coverage and practicality.
152#[cfg(test)]
153const PROPTEST_CASES: u32 = if cfg!(debug_assertions) {0x1_0000} else {0x80_0000};