1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
//! This module and its submodules contain a software implementation of a standard-compliant Posit
//! floating point type, with arbitrary width and exponent width (up to 128).
//!
//! This module is **EXTENSIVELY** documented! If you want to learn more about Posits and an
//! optimised software implementation thereof (or about floating point implementations in
//! general!), you might profit from reading carefully through the code :) We assume basic
//! familiarity with both the Posit format and with two's complement integer arithmetic;
//! everything else we try to explain.
//!
//! If you know nothing about Posits and want to learn more, a good place to start is
//! <https://posithub.org/docs/Posits4.pdf>. The most up to date standard is at
//! <https://posithub.org/docs/posit_standard-2.pdf>.
//!
//! Some notation used in the comments:
//!
//! - **Leftmost bits/msb**: most-significant bits.
//! - **Rightmost bits/lsb**: least-significant bits.
//! - **Bit 0, bit 1, .. bit N-1**: numbered least significant to most significant.
//! - [a; b[ for a range that is closed (inclusive) on `a` and open (exclusive) on `b`.
//!
//! Suggested reading order: [Posit] and [Decoded] types, [basics] and [constants](consts),
//! [decode] implementation (Posit→Decoded), [encode] implementation (Decoded→Posit),
//! [elementary arithmetic](ops).
//!
//! # Testing
//!
//! Extensive tests are included. When possible, we check all inputs exhaustively (e.g.: adding two
//! 8-bit posits, there are only 256² possible inputs). Whenever this is not feasible (e.g: adding
//! two 64-bit posits), we make use of the `proptest` crate, which allows us to probabilistically
//! test inputs from a random distribution.
//!
//! Another key ingredient in the testing process is in the `rational` submodule. There, we define
//! some tools to convert to and from *arbitrary precision* rational numbers. Since any non-NaR
//! posit is a rational number, we can test any function in this crate involving posits by simply
//! checking that the output matches the output of the equivalent function using
//! infinite-precision rationals. For instance: to test addition of two posits, we simply check
//! that `rational(a) + rational(b) == rational(a + b)`, i.e. adding the posits yields the same
//! result as adding the arbitrary-precision rationals (modulo rounding, of course).
/// A *posit* floating point number with `N` bits and `ES` exponent bits, optionally with a
/// maximum regime size `RS` (making it a *b-posit*).
///
// A posit is an alternative floating point format first proposed in 2017
//
/// This is a generic type which can be instantiated with any valid combination of parameters
/// (see below). It implements the functions described in the [2022 standard] (and more), either
/// via the usual arithmetic operations (addition, negation, comparison, etc) or via named
/// functions. Rounding conversions to and from other types (integers, IEEE floats, etc) are done
/// via the [`RoundFrom`](crate::RoundFrom) and [`RoundInto`](crate::RoundInto) traits, which need
/// to be in scope.
///
/// Refer to the documentation for specific items for more detail, examples, and links to the
/// definitions in the [2022 standard], if applicable.
///
/// If you are looking for [the types prescribed in the 2022 standard], see [`p8`](crate::p8),
/// [`p16`](crate::p16), [`p32`](crate::p32), and [`p64`](crate::p64), which are simply type
/// aliases for [`Posit<X, 2, iX>`].
///
/// [the types prescribed in the 2022 standard]: https://posithub.org/docs/posit_standard-2.pdf#subsection.3.1
///
/// # Parameters
///
/// The `N` and `ES` parameters are mandatory, and the `RS` parameter is optional. The parameter
/// `Int` is the underlying physical storage used for the posit, which must be one of the
/// primitive types `i8` to `i128`.
///
/// If `Int` = `iX`, then
///
/// - `N`, the number of bits, must be in the range `3 ..= X`,
/// - `ES`, the number of exponent bits, must be in the range `0 .. N`.
/// - `RS`, the maximum number of regime bits, if supplied, must be in the range `1 ..= N`,
/// otherwise it will default to `N`(unbounded).
///
/// If any parameter is invalid, a **compile-time** error will be raised.
///
/// # Example
///
/// ```
/// # use fast_posit::Posit;
/// // A 32-bit posit with 2-bit exponent field, represented in a 32-bit machine type.
/// type Foo = Posit<32, 2, i32>;
/// // A 6-bit posit with 1-bit exponent field, represented in an 8-bit machine type.
/// type Bar = Posit<6, 1, i8>;
/// // A 32-bit b-posit with 3-bit exponent field and at most a 6-bit regime field, represented in a 32-bit machine type.
/// type Baz = Posit<32, 3, i32, 6>;
/// ```
///
/// Note that `Posit` will have the same size (and alignment) as its `Int` parameter, so it's
/// currently not possible to create e.g. a 4-bit posit that only actually takes 4 bits in memory.
///
/// These are examples of invalid parameters which will not compile.
///
/// ```compile_fail
/// # use fast_posit::Posit;
/// type Foo = Posit<40, 2, i32>; // N=40 too big for i32
/// # let _ = Foo::ONE;
/// ```
/// ```compile_fail
/// # use fast_posit::Posit;
/// type Bar = Posit<1, 0, i8>; // N=1 can't be ≤ 2
/// # let _ = Bar::ONE;
/// ```
/// ```compile_fail
/// # use fast_posit::Posit;
/// type Baz = Posit<32, 33, i32>; // ES=33 too big for N=32
/// # let _ = Baz::ONE + Baz::ONE;
/// ```
/// ```compile_fail
/// # use fast_posit::Posit;
/// type Qux = Posit<32, 2, 33, i32>; // RS=33 too big for N=32
/// # let _ = Qux::ONE + Qux::ONE;
/// ```
///
/// [2022 standard]: https://posithub.org/docs/posit_standard-2.pdf
;
/// In order to perform most nontrivial operations, a `Posit<N, ES, Int>` needs to be *decoded*
/// into the form `f × 2^e` (with rational fraction `f` and integer exponent `e`), a form that is
/// amenable for further manipulation.
///
/// This is represented as a `Decoded<N, ES, Int>`, a struct that contains two integer fields,
/// `frac` and `exp`, such that it represents the value
///
/// ```text
/// (frac / FRAC_DENOM) × (2 ^ exp)
/// ```
///
/// where `FRAC_DENOM` is a fixed power of two, equal to `2 ^ (B-2)`, where `B` = `Int::BITS`.
///
/// That is to say: this encodes the `f × 2^e` referred above using two integers:
/// - The integer [`exp`](Self::exp) is the integer `e`, and
/// - The integer [`frac`](Self::frac) is the rational `f` *with an implicit denominator* of
/// `1 << (B-2)`.
///
/// Another way to think of it is that `frac` is a fixed-point rational number, where the dot is
/// two places from the left. For instance (for an 8-bit `frac`):
///
/// - `0b01_000000` = +1.00
/// - `0b01_100000` = +1.50
/// - `0b10_000000` = -2.00
/// - `0b11_100000` = -0.50
///
/// and so on.
///
/// The docstrings for [both](Decoded::frac) [fields](Decoded::exp) contain more detail, more
/// examples, and further considerations about their values.
///
/// Extracting these fields from a posit, and converting back to a posit with correct rounding, can
/// be done **very** efficiently, and indeed those two algorithms lie at the heart of many
/// operations: [`Posit::decode_regular`] and [`Decoded::encode_regular_round`].
/// Some basic constants and functions, such as a check that `N` and `ES` make sense for `Int`, or
/// functions to convert to/from raw bits.
/// Manual implementation of `derive`able traits for [`Posit`]. This is because the derive macro
/// isn't smart enough to figure some transitive bounds in `Int` → `Sealed`, so we would be left
/// with superfluous bounds on those traits.
/// Numeric constants: zero, NaR, min, min_positive, etc.
/// [`Posit`] → [`Decoded`].
/// [`Decoded`] → [`Posit`], including correct rounding.
/// Small fns of one posit argument: neg, prior, next, is_positive, etc.
/// Rounding a posit to integer-valued posit: round_int, floor, ceil.
/// The four basic arithmetic operations: +, -, ×, ÷.
/// Other mathematical operations: sqrt, log, exp, sin, etc.
/// Quire (the fixed-point accumulator for error-free sums and dot products)
/// Conversions to and from integers, to and from floats, and between different posit types.
///
/// Two sorts of conversions are implemented:
/// - The conversions prescribed in the posit standard (using `round_from`).
/// - The "Rusty" conversions (using `from` for unfallible conversions and `try_from` for
/// fallible ones).
///
/// The [convert::RoundFrom] and [convert::RoundInto] traits are defined here.
/// Traits for [`core::fmt::Display`] and [`core::fmt::Debug`].
/// Conversions to and from an arbitrary-precision [malachite::rational::Rational], for testing
/// purposes. This enables us to verify our algorithms by checking that the exact rationals match.
/// For example:
///
/// - rational(p1 + p2) = rational(p1) + rational(p2)
/// - rational(p1::ONE) = rational(1)
/// - rational(p1) = rational(decoded(p1))
/// Miscellaneous helpers for testing.