Skip to main content

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