fast_posit/posit/
basics.rs

1use super::*;
2use crate::underlying::const_as;
3
4impl<
5  const N: u32,
6  const ES: u32,
7  Int: crate::Int,
8> Posit<N, ES, Int> {
9  /// The size of this Posit type in bits (i.e. parameter `N`).
10  ///
11  /// Note: this is the logical size, not necessarily the size of the underlying type.
12  ///
13  /// # Example
14  ///
15  /// ```
16  /// # use fast_posit::*;
17  /// assert_eq!(p16::BITS, 16);                  // Standard posit
18  /// assert_eq!(Posit::<20, 1, i32>::BITS, 20);  // Non-standard posit
19  /// ```
20  pub const BITS: u32 = {
21    assert!(
22      N >= 3,
23      "A posit cannot have fewer than 3 bits",
24    );
25    assert!(
26      N <= Int::BITS,
27      "Cannot represent an n-bit Posit with an underlying Int machine type with fewer bits.",
28    );
29    N
30  };
31
32  /// The number of exponent bits (i.e. parameter `ES`).
33  ///
34  /// # Example
35  ///
36  /// ```
37  /// # use fast_posit::*;
38  /// assert_eq!(p16::ES, 2);                  // Standard posit
39  /// assert_eq!(Posit::<20, 1, i32>::ES, 1);  // Non-standard posit
40  /// ```
41  pub const ES: u32 = {
42    assert!(
43      ES <= N,
44      "Cannot use a number of exponent bits ES higher than the number of total bits N",
45    );
46    // The value of ES isn't completely arbitrary. Very extreme values of ES would cause the maximum
47    // exponent to overflow the width of the `Int` type. Therefore, we check this at compile-time.
48    //
49    // The maximum exponent is 2 ^ Self::MAX_EXP. However, for guarding against overflow in all
50    // operations in the Posit standard, it's also really helpful to represent quantities up to
51    // (2 ^ Self::MAX_EXP) ^ 3 = 2 ^ (3 * Self::MAX_EXP). Rounding up to a clean number, we require
52    // the number 4 * Self::MAX_EXP (exclusive) to be representable in an `Int`.
53    //
54    // Self::MAX_EXP is (N-2) * 2^ES, so our requirement is 4 * (N-2) * 2^ES < 2 ^ Int::BITS, or
55    // N - 2 < 2 ^ (Int::BITS - ES - 2).
56    //
57    // To make Rust allow this to go in compile-time (const), we round (N-2) down to the nearest
58    // power of two and take the log, i.e. we check 2 ^ floor(log(N-2)) < 2 ^ (Int::BITS - ES - 2)
59    // or finally floor(log(N-2)) < Int::BITS - ES - 2.
60    assert!(
61      (N - 2).ilog2() + ES + 2 < Int::BITS,
62      "The chosen ES is too big for this combination of N and underlying Int type. Consider \
63      lowering the number of exponent bits, or choosing a bigger underlying Int if you really \
64      want this many.",
65    );
66    ES
67  };
68
69  /// When representing an `N`-bit posit using a machine type whose width is `M`, the leftmost
70  /// `N - M` bits are junk; they are always the same as the bit `N-1` (the function
71  /// [`Self::sign_extend`] maintains this invariant).
72  ///
73  /// In other words, the range of the `Int` in `Posit<N, ES, Int>` is from `-2^N` to `+2^N - 1`.
74  ///
75  /// Of course, if [`Self::BITS`] is exactly as wide as the underlying `Int::BITS` (as is vastly
76  /// the more common case), this is `0`.
77  pub(crate) const JUNK_BITS: u32 = Int::BITS - Self::BITS;
78
79  /// Take an `Int` and sign-extend from [`Self::BITS`] (logical width of posit) to `Int::BITS`.
80  #[inline]
81  pub(crate) /*const*/ fn sign_extend(x: Int) -> Int {
82    if const { Self::JUNK_BITS == 0 } {
83      x
84    } else {
85      (x << Self::JUNK_BITS) >> Self::JUNK_BITS
86    }
87  }
88
89  /// Return the underlying bit representation of `self` as a machine int. Bits higher
90  /// (more significant) than the lowest `N` bits, if any, are set as equal to the `N-1`th bit
91  /// (i.e. sign-extended).
92  ///
93  /// # Example
94  ///
95  /// ```
96  /// # #![allow(overflowing_literals)]
97  /// # use fast_posit::*;
98  /// assert_eq!(0b00000000, p8::ZERO.to_bits());
99  /// assert_eq!(0b01000000, p8::ONE.to_bits());
100  /// assert_eq!(0b01111111, p8::MAX.to_bits());
101  /// assert_eq!(0b00000001, p8::MIN_POSITIVE.to_bits());
102  /// assert_eq!(0b11000000, p8::MINUS_ONE.to_bits());
103  /// assert_eq!(0b10000001, p8::MIN.to_bits());
104  /// assert_eq!(0b11111111, p8::MAX_NEGATIVE.to_bits());
105  /// assert_eq!(0b10000000, p8::NAR.to_bits());
106  /// ```
107  #[inline]
108  pub const fn to_bits(self) -> Int {
109    self.0
110  }
111
112  /// Construct a posit from its raw bit representation. Bits higher (more significant) than the
113  /// lowest `N` bits, if any, are ignored.
114  ///
115  /// # Example
116  ///
117  /// ```
118  /// # #![allow(overflowing_literals)]
119  /// # use fast_posit::*;
120  /// assert_eq!(p8::from_bits(0), p8::ZERO);
121  /// assert_eq!(p8::from_bits(1), p8::MIN_POSITIVE);
122  /// assert_eq!(p8::from_bits(i8::MAX), p8::MAX);
123  /// assert_eq!(p8::from_bits(0b0_10_01_011), 2.75.round_into());
124  /// assert_eq!(p8::from_bits(0b1_110_00_01), (-0.0546875).round_into());
125  /// ```
126  #[inline]
127  pub /*const*/ fn from_bits(bits: Int) -> Self {
128    Self(Self::sign_extend(bits))
129  }
130
131  /// As [`Self::from_bits`], but does not check that `bits` is a valid bit pattern for `Self`.
132  ///
133  /// # Safety
134  ///
135  /// `bits` has to be a result of a [`Self::to_bits`] call, i.e. it has to be in the range 
136  /// `-1 << (N-1) ..= 1 << (N-1) - 1`, or calling this function is *undefined behaviour*. Note
137  /// that if `Int::BITS == Self::BITS` this always holds.
138  ///
139  /// # Example
140  ///
141  /// ```
142  /// # #![allow(overflowing_literals)]
143  /// # use fast_posit::*;
144  /// type Posit4 = Posit<4, 1, i8>;
145  /// assert_eq!(Posit4::from_bits(0b0000_0100), Posit4::ONE);
146  /// assert_eq!(Posit4::from_bits(0b1111_1000), Posit4::NAR);
147  /// ```
148  ///
149  /// but the following would not be valid as the bits are not in a valid range (i.e. not
150  /// sign-extended past 4 bits).
151  ///
152  /// ```no_run
153  /// # #![allow(overflowing_literals)]
154  /// # use fast_posit::*;
155  /// # type Posit4 = Posit<4, 1, i8>;
156  /// Posit4::from_bits(0b0110_0100);  // Undefined behaviour!
157  /// ```
158  #[inline]
159  pub const unsafe fn from_bits_unchecked(bits: Int) -> Self {
160    Self(bits)
161  }
162
163  /// Checks whether `self` is an exception ([0](Self::ZERO) or [NaR](Self::NAR)), that is, the
164  /// same as `self == Self::ZERO || self == Self::NAR`, but faster.
165  #[inline]
166  pub(crate) fn is_special(&self) -> bool {
167    (self.0 << Self::JUNK_BITS) << 1 == Int::ZERO
168  }
169}
170
171impl<
172  const N: u32,
173  const ES: u32,
174  Int: crate::Int,
175> Decoded<N, ES, Int> {
176  /// The [`Decoded::frac`] field has the decimal point [`Decoded::FRAC_WIDTH`] bits from the
177  /// right.
178  ///
179  /// If you're unsure what this means, refer to the documentation for the
180  /// [`frac`][Decoded::frac] field.
181  pub(crate) const FRAC_WIDTH: u32 = Int::BITS - 2;
182
183  /// The [`Decoded::frac`] field represents the fraction/mantissa of a posit as a fixed-point
184  /// number. [`Decoded::FRAC_DENOM`] is the denominator of that fixed-point number.
185  ///
186  /// If you're unsure what this means, refer to the documentation for the
187  /// [`frac`][Decoded::frac] field.
188  pub(crate) const FRAC_DENOM: Int = const_as(1i128 << Self::FRAC_WIDTH);
189
190  // TODO MIN/MAX_EXP? Used a couple of times
191
192  /// As [`Posit::BITS`].
193  pub const BITS: u32 = Posit::<N, ES, Int>::BITS;
194
195  /// As [`Posit::ES`].
196  pub const ES: u32 = Posit::<N, ES, Int>::ES;
197
198  /// As [`Posit::JUNK_BITS`].
199  pub(crate) const JUNK_BITS: u32 = Posit::<N, ES, Int>::JUNK_BITS;
200
201  /// Checks whether `self` is "normalised", i.e. whether 
202  ///
203  /// - `self.frac` starts with `0b01` or `0b10`, and
204  /// - `self.exp >> ES` starts with `0b00` or `0b11` (which is guaranteed if `ES > 0`).
205  pub(crate) fn is_normalised(self) -> bool {
206    let frac = self.frac >> Self::FRAC_WIDTH;
207    let exp = self.exp >> Self::FRAC_WIDTH;
208    (frac == Int::ONE || frac == !Int::ONE) && (ES > 0 || exp == Int::ZERO || exp == !Int::ZERO)
209  }
210}
211
212#[cfg(test)]
213mod tests {
214  use super::*;
215
216  #[test]
217  fn bits() {
218    assert_eq!(Posit::<8, 2, i8>::BITS, 8);
219    assert_eq!(Posit::<16, 2, i16>::BITS, 16);
220    assert_eq!(Posit::<32, 2, i32>::BITS, 32);
221    assert_eq!(Posit::<64, 2, i64>::BITS, 64);
222    assert_eq!(Posit::<128, 2, i128>::BITS, 128);
223
224    assert_eq!(Posit::<8, 0, i8>::BITS, 8);
225    assert_eq!(Posit::<16, 1, i16>::BITS, 16);
226    assert_eq!(Posit::<32, 2, i32>::BITS, 32);
227    assert_eq!(Posit::<64, 3, i64>::BITS, 64);
228    assert_eq!(Posit::<128, 4, i128>::BITS, 128);
229
230    assert_eq!(Posit::<6, 1, i8>::BITS, 6);
231    assert_eq!(Posit::<10, 2, i64>::BITS, 10);
232    assert_eq!(Posit::<32, 2, i64>::BITS, 32);
233  }
234
235  #[test]
236  fn es() {
237    assert_eq!(Posit::<8, 2, i8>::ES, 2);
238    assert_eq!(Posit::<16, 2, i16>::ES, 2);
239    assert_eq!(Posit::<32, 2, i32>::ES, 2);
240    assert_eq!(Posit::<64, 2, i64>::ES, 2);
241    assert_eq!(Posit::<128, 2, i128>::ES, 2);
242
243    assert_eq!(Posit::<8, 0, i8>::ES, 0);
244    assert_eq!(Posit::<16, 1, i16>::ES, 1);
245    assert_eq!(Posit::<32, 2, i32>::ES, 2);
246    assert_eq!(Posit::<64, 3, i64>::ES, 3);
247    assert_eq!(Posit::<128, 4, i128>::ES, 4);
248
249    assert_eq!(Posit::<6, 1, i8>::ES, 1);
250    assert_eq!(Posit::<10, 2, i64>::ES, 2);
251    assert_eq!(Posit::<32, 2, i64>::ES, 2);
252  }
253
254  #[test]
255  fn es_max() {
256    assert_eq!(Posit::<8, 3, i8>::ES, 3);
257    assert_eq!(Posit::<16, 10, i16>::ES, 10);
258    assert_eq!(Posit::<32, 25, i32>::ES, 25);
259    assert_eq!(Posit::<64, 56, i64>::ES, 56);
260    assert_eq!(Posit::<128, 119, i128>::ES, 119);
261
262    assert_eq!(Posit::<8, 8, i16>::ES, 8);
263    assert_eq!(Posit::<16, 16, i32>::ES, 16);
264    assert_eq!(Posit::<32, 32, i64>::ES, 32);
265    assert_eq!(Posit::<64, 64, i128>::ES, 64);
266  }
267
268  #[test]
269  #[allow(overflowing_literals)]
270  fn from_bits() {
271    fn assert_roundtrip<const N: u32, const ES: u32, Int: crate::Int>(a: Int, b: Int) {
272      assert_eq!(Posit::<N, ES, Int>::from_bits(a).to_bits(), b)
273    }
274
275    // N = width of type
276    assert_roundtrip::<16, 2, i16>(
277      0b0000_0101_0011_1010,
278      0b0000_0101_0011_1010,
279    );
280    assert_roundtrip::<16, 2, i16>(
281      0b1111_0101_0011_1010,
282      0b1111_0101_0011_1010,
283    );
284    assert_roundtrip::<16, 2, i16>(
285      0b0101_0011_0011_1010,
286      0b0101_0011_0011_1010,
287    );
288
289    // N < width of type (needs sign-extension to bits ≥ 10)
290    assert_roundtrip::<10, 2, i16>(
291      0b000001_01_0011_1010,
292      0b000000_01_0011_1010,
293    );
294    assert_roundtrip::<10, 2, i16>(
295      0b111101_01_0011_1010,
296      0b000000_01_0011_1010,
297    );
298    assert_roundtrip::<10, 2, i16>(
299      0b010100_11_0011_1010,
300      0b111111_11_0011_1010,
301    );
302  }
303}
304
305mod tests_compile_fail {
306  /// ```compile_fail
307  /// use fast_posit::Posit;
308  /// pub fn foo() -> u32 { Posit::<2, 0, i8>::BITS }
309  /// ```
310  #[allow(dead_code)]
311  fn bits_fail_8_few() {}
312
313  /// ```compile_fail
314  /// use fast_posit::Posit;
315  /// pub fn foo() -> u32 { Posit::<2, 1, i16>::BITS }
316  /// ```
317  #[allow(dead_code)]
318  fn bits_fail_16_few() {}
319
320  /// ```compile_fail
321  /// use fast_posit::Posit;
322  /// pub fn foo() -> u32 { Posit::<2, 2, i32>::BITS }
323  /// ```
324  #[allow(dead_code)]
325  fn bits_fail_32_few() {}
326
327  /// ```compile_fail
328  /// use fast_posit::Posit;
329  /// pub fn foo() -> u32 { Posit::<2, 3, i64>::BITS }
330  /// ```
331  #[allow(dead_code)]
332  fn bits_fail_64_few() {}
333
334  /// ```compile_fail
335  /// use fast_posit::Posit;
336  /// pub fn foo() -> u32 { Posit::<2, 4, i128>::BITS }
337  /// ```
338  #[allow(dead_code)]
339  fn bits_fail_128_few() {}
340
341  //
342
343  /// ```compile_fail
344  /// use fast_posit::Posit;
345  /// pub fn foo() -> u32 { Posit::<9, 0, i8>::BITS }
346  /// ```
347  #[allow(dead_code)]
348  fn bits_fail_8_many() {}
349
350  /// ```compile_fail
351  /// use fast_posit::Posit;
352  /// pub fn foo() -> u32 { Posit::<17, 1, i16>::BITS }
353  /// ```
354  #[allow(dead_code)]
355  fn bits_fail_16_many() {}
356
357  /// ```compile_fail
358  /// use fast_posit::Posit;
359  /// pub fn foo() -> u32 { Posit::<33, 2, i32>::BITS }
360  /// ```
361  #[allow(dead_code)]
362  fn bits_fail_32_many() {}
363
364  /// ```compile_fail
365  /// use fast_posit::Posit;
366  /// pub fn foo() -> u32 { Posit::<65, 3, i64>::BITS }
367  /// ```
368  #[allow(dead_code)]
369  fn bits_fail_64_many() {}
370
371  /// ```compile_fail
372  /// use fast_posit::Posit;
373  /// pub fn foo() -> u32 { Posit::<129, 4, i128>::BITS }
374  /// ```
375  #[allow(dead_code)]
376  fn bits_fail_128_many() {}
377
378  //
379
380  /// ```compile_fail
381  /// use fast_posit::Posit;
382  /// pub fn foo() -> u32 { Posit::<8, 4, i8>::ES }
383  /// ```
384  #[allow(dead_code)]
385  fn es_fail_8_many() {}
386
387  /// ```compile_fail
388  /// use fast_posit::Posit;
389  /// pub fn foo() -> u32 { Posit::<16, 11, i16>::ES }
390  /// ```
391  #[allow(dead_code)]
392  fn es_fail_16_many() {}
393
394  /// ```compile_fail
395  /// use fast_posit::Posit;
396  /// pub fn foo() -> u32 { Posit::<32, 26, i32>::ES }
397  /// ```
398  #[allow(dead_code)]
399  fn es_fail_32_many() {}
400
401  /// ```compile_fail
402  /// use fast_posit::Posit;
403  /// pub fn foo() -> u32 { Posit::<64, 57, i64>::ES }
404  /// ```
405  #[allow(dead_code)]
406  fn es_fail_64_many() {}
407
408  /// ```compile_fail
409  /// use fast_posit::Posit;
410  /// pub fn foo() -> u32 { Posit::<128, 120, i128>::ES }
411  /// ```
412  #[allow(dead_code)]
413  fn es_fail_128_many() {}
414
415  //
416
417  /// ```compile_fail
418  /// use fast_posit::Posit;
419  /// pub fn foo() -> u32 { Posit::<8, 9, i16>::ES }
420  /// ```
421  #[allow(dead_code)]
422  fn es_fail_8_larger() {}
423
424  /// ```compile_fail
425  /// use fast_posit::Posit;
426  /// pub fn foo() -> u32 { Posit::<16, 17, i32>::ES }
427  /// ```
428  #[allow(dead_code)]
429  fn es_fail_16_larger() {}
430
431  /// ```compile_fail
432  /// use fast_posit::Posit;
433  /// pub fn foo() -> u32 { Posit::<32, 33, i64>::ES }
434  /// ```
435  #[allow(dead_code)]
436  fn es_fail_32_larger() {}
437
438  /// ```compile_fail
439  /// use fast_posit::Posit;
440  /// pub fn foo() -> u32 { Posit::<64, 65, i128>::ES }
441  /// ```
442  #[allow(dead_code)]
443  fn es_fail_64_larger() {}
444}