Skip to main content

fast_posit/posit/quire/
basics.rs

1use super::*;
2
3impl<
4  const N: u32,
5  const ES: u32,
6  const SIZE: usize,
7> Quire<N, ES, SIZE> {
8  /// The quire size in bits.
9  ///
10  /// # Example
11  ///
12  /// ```
13  /// # use fast_posit::*;
14  /// assert_eq!(q16::BITS, 256);
15  /// ```
16  pub const BITS: u32 = Self::SIZE as u32 * 8;
17
18  /// The quire size in bytes (i.e. parameter `SIZE`).
19  ///
20  /// # Example
21  ///
22  /// ```
23  /// # use fast_posit::*;
24  /// assert_eq!(q16::SIZE, 32);
25  /// ```
26  pub const SIZE: usize = {
27    assert!(SIZE >= Self::MIN_SIZE, "This quire type has fewer than the minimum number of bytes");
28    // if const { SIZE < Self::MIN_SIZE } { compile_error!("asdf") }
29    SIZE
30  };
31
32  /// Construct a quire from its raw bit representation, as a byte array in little-endian order.
33  ///
34  /// # Example
35  ///
36  /// ```
37  /// # use fast_posit::*;
38  /// let quire = q8::from_le_bytes([0,0,0,0,0,0, 1,0,0,0,0,0, 0,0,0,0]);
39  /// assert_eq!(p8::round_from(&quire), p8::ONE);
40  /// ```
41  #[inline]
42  pub const fn from_le_bytes(bytes: [u8; SIZE]) -> Self {
43    Self(bytes)
44  }
45
46  /// Construct a quire from its raw bit representation, as a byte array in big-endian order.
47  ///
48  /// # Example
49  ///
50  /// ```
51  /// # use fast_posit::*;
52  /// let quire = q8::from_be_bytes([0,0,0,0, 0,0,0,0,0,1, 0,0,0,0,0,0]);
53  /// assert_eq!(p8::round_from(&quire), p8::ONE);
54  /// ```
55  #[inline]
56  pub const fn from_be_bytes(mut bytes: [u8; SIZE]) -> Self {
57    bytes.as_mut_slice().reverse();
58    Self::from_le_bytes(bytes)
59  }
60
61  /// Return the raw bit representation of a quire, as a byte array in little-endian order.
62  ///
63  /// # Example
64  ///
65  /// ```
66  /// # use fast_posit::*;
67  /// let bytes = q8::NAR.to_le_bytes();
68  /// assert_eq!(bytes, [0,0,0,0,0,0, 0,0,0,0,0,0, 0,0,0,0x80]);
69  /// ```
70  #[inline]
71  pub const fn to_le_bytes(self) -> [u8; SIZE] {
72    self.0
73  }
74
75  /// The quire size in u64s (= [`BITS`](Self::BITS) / 64).
76  pub(crate) const LEN_U64: usize = Self::BITS as usize / 64;
77
78  /// Access the storage as an array of `u64`s, in little endian order.
79  ///
80  /// Limitation: even though the return size is known, we cannot return an `&[u64; N]` due to
81  /// limitations in the Rust type system. We have to hope that the compiler will inline and fold
82  /// the slice len :)
83  #[inline(always)]
84  pub(crate) const fn as_u64_array(&self) -> &[u64] {
85    const { assert!(SIZE.is_multiple_of(8), "Quire SIZE must be a multiple of 64 bits (8 bytes)"); }
86    const { assert!(cfg!(target_endian = "little"), "Big-endian targets are not currently supported") }
87    let ptr = self.0.as_ptr() as *const u64;
88    // SAFETY: ptr and len form a valid slice; the size and alignment is correct, and any bit
89    // pattern is a valid u64 value.
90    unsafe { core::slice::from_raw_parts(ptr, Self::LEN_U64) }
91  }
92
93  #[inline(always)]
94  pub(crate) const fn as_u64_array_mut(&mut self) -> &mut [u64] {
95    const { assert!(SIZE.is_multiple_of(8), "Quire SIZE must be a multiple of 64 bits (8 bytes)"); }
96    const { assert!(cfg!(target_endian = "little"), "Big-endian targets are not currently supported") }
97    let ptr = self.0.as_mut_ptr() as *mut u64;
98    // SAFETY: ptr and len form a valid slice; the size and alignment is correct, and any bit
99    // pattern is a valid u64 value.
100    unsafe { core::slice::from_raw_parts_mut(ptr, Self::LEN_U64) }
101  }
102
103  #[inline(always)]
104  pub(crate) const fn as_int_array<Int: crate::Int>(&self) -> &[Int] {
105    const { assert!(Self::BITS % Int::BITS == 0, "Quire BITS must be a multiple of Int bits"); }
106    const { assert!(cfg!(target_endian = "little"), "Big-endian targets are not currently supported") }
107    let ptr = self.0.as_ptr() as *const Int;
108    // SAFETY: ptr and len form a valid slice; the size and alignment is correct, and any bit
109    // pattern is a valid Int value.
110    unsafe { core::slice::from_raw_parts(ptr, Self::BITS as usize / Int::BITS as usize) }
111  }
112
113  /// Auxiliary const: the maximum (positive) exponent of a `Posit<N, ES, Int>`. The size of the
114  /// quire is directly related to this (see [`Self::MIN_SIZE`] and [`Self::WIDTH`] below).
115  pub(crate) const MAX_EXP: u32 = {
116    assert!(ES < 20, "Cannot use the quire with very high ES (≥ 20)");
117    let max_exp = Posit::<N, ES, i128>::MAX_EXP;
118    if 0 <= max_exp && max_exp < 1 << 31 {
119      max_exp as u32
120    } else {
121      unreachable!()
122    }
123  };
124
125  /// The minimum [`SIZE`](Self::SIZE) of a quire for [`Posit<N, ES, Int>`], in bytes.
126  ///
127  /// # Example
128  ///
129  /// ```
130  /// # use fast_posit::*;
131  /// assert_eq!(q16::MIN_SIZE, 29);
132  /// ```
133  pub const MIN_SIZE: usize = {
134    // At worst, need to be able to represent [Posit::MAX]² + [Posit::MIN]² as a fixed point
135    // number. So that's a number of bits equal to 2×2×max_exp. Then add 1 sign bit: that's the
136    // minimum quire size in bits.
137    let min_size_bits = 4 * Self::MAX_EXP + 1;
138    min_size_bits.div_ceil(8) as usize
139  };
140
141  /// The minimum number of operations on the quire that can lead to overflow is
142  /// 2 <sup>[`PROD_LIMIT`](Self::PROD_LIMIT)</sup>; any number of [`Self::add_prod`] calls
143  /// smaller than that is *guaranteed* not to overflow.
144  ///
145  /// # Example
146  ///
147  /// ```
148  /// # use fast_posit::*;
149  /// assert_eq!(q32::PROD_LIMIT, 31);  // Can do at least 2^31 - 1 products without overflow
150  /// ```
151  pub const PROD_LIMIT: u32 = {
152    // The biggest possible product (Posit::MAX * Posit::MAX) takes `4 * MAX_EXP` bits. It can be
153    // accumulated `2 ^ M` times, where `M` is the difference between that and this quire's
154    // `SIZE`, before it overflows.
155    let min_size_bits = 4 * Self::MAX_EXP + 1;
156    Self::BITS - min_size_bits
157  };
158
159  /// The minimum number of additions of posits that can lead to overflow is
160  /// 2 <sup>[`SUM_LIMIT`](Self::SUM_LIMIT)</sup>; any number of `+=` or `-=` operations smaller
161  /// than that is *guaranteed* not to overflow.
162  ///
163  /// # Example
164  ///
165  /// ```
166  /// # use fast_posit::*;
167  /// assert_eq!(q32::SUM_LIMIT, 151);  // Can sum at least 2^151 - 1 terms without overflow
168  /// ```
169  pub const SUM_LIMIT: u32 = {
170    // The biggest possible posit value (Posit::MAX) takes `3 * MAX_EXP` bits. It can be
171    // accumulated `2 ^ M` times, where `M` is the difference between that and this quire's
172    // `SIZE`, before it overflows.
173    let min_size_bits = 3 * Self::MAX_EXP + 1;
174    Self::BITS - min_size_bits
175  };
176
177  /// The position of the fixed point, that is: "1.0" is represented in the quire as `1 << WIDTH`.
178  pub(crate) const WIDTH: u32 = {
179    let _ = Self::SIZE;
180    2 * Self::MAX_EXP
181  };
182
183  // TODO assert this on operations with posits (cannot assert here because needs to take into
184  // account the posit's underlying Int):
185  // assert!(SIZE % N == 0, "The size of the quire type is not a multiple of the posit size");
186
187  /// The quire that represents the posit number [0](Posit::ZERO).
188  pub const ZERO: Self = Self([0; SIZE]);
189
190  /// The quire that represents the posit value [`NaR`](Posit::NAR).
191  pub const NAR: Self = {
192    let mut nar = Self::ZERO;
193    nar.0[Self::SIZE - 1] = i8::MIN as u8;
194    nar
195  };
196
197  /// The quire with the greatest value, equal to `-Self::MIN`.
198  ///
199  /// Any sum of a positive value (be it posit, product of posits, or quire) onto
200  /// [`MAX`](Self::MAX) will result in overflow, making the result [`NaR`][Self::NAR].
201  ///
202  /// # Example
203  ///
204  /// ```
205  /// # use fast_posit::*;
206  /// let mut quire = q32::MAX;
207  /// quire += p32::MIN_POSITIVE;
208  /// assert!(quire.is_nar())
209  /// ```
210  pub const MAX: Self = {
211    let mut bytes_le = [0xff; SIZE];
212    bytes_le[SIZE - 1] = 0x7f;
213    Self::from_le_bytes(bytes_le)
214  };
215
216  /// The quire with the smallest value, equal to `-Self::MAX`.
217  ///
218  /// Any sum of a negative value (be it posit, product of posits, or quire) onto
219  /// [`MIN`](Self::MIN) will result in overflow, making the result [`NaR`][Self::NAR].
220  ///
221  /// # Example
222  ///
223  /// ```
224  /// # use fast_posit::*;
225  /// let mut quire = q32::MIN;
226  /// quire -= p32::MIN_POSITIVE;
227  /// assert!(quire.is_nar())
228  /// ```
229  pub const MIN: Self = {
230    let mut bytes_le = [0x00; SIZE];
231    bytes_le[SIZE - 1] = 0x80;
232    bytes_le[0] |= 0x01;
233    Self::from_le_bytes(bytes_le)
234  };
235
236  /// Checks whether `self` represents a NaR value.
237  ///
238  /// # Example
239  ///
240  /// ```
241  /// # use fast_posit::*;
242  /// assert!(q32::NAR.is_nar());
243  /// ```
244  pub const fn is_nar(&self) -> bool {
245    let _ = Self::NAR;
246    // This is more optimised than it looks. If the quire is not NaR, which is the "normal" and
247    // thus more important case to optimise, then most likely it will either start with
248    // `0b00…001…` (positive) of `0b11…110…` (negative), and thus return right away on the first
249    // if statement. This is because the only way it starts with `0b1000…` and yet is not NaR is
250    // if it's very very close to overflowing on the negative side; this is *exceedingly*
251    // unlikely.
252    //
253    // Therefore, for almost all cases where the quire is not NaR, we only need a compare and
254    // branch. Only on when the quire is NaR, or in the rare cases where it's not NaR but still
255    // starts with `0b1000…`, will we need to scan through the whole thing.
256    let quire: &[u64] = self.as_u64_array();
257    if crate::utl::likely(quire[quire.len() - 1] != i64::MIN as u64) {
258      false
259    } else {
260      self.is_nar_slow()
261    }
262  }
263
264  // #[cold]
265  // #[inline(never)]
266  const fn is_nar_slow(&self) -> bool {
267    // Written in this awkward way because it's a `const fn`...
268    let quire: &[u64] = self.as_u64_array();
269    let mut i = 0;
270    let mut acc = 0;
271    while i < quire.len() - 1 {
272      acc |= quire[i];
273      i += 1
274    }
275    acc == 0
276  }
277
278  /// Equivalent to `*self = Self::NAR`, but on a cold branch to ensure it does NOT get inlined and
279  /// pollute the i-cache.
280  #[cold]
281  #[inline(never)]
282  pub(crate) fn set_nar(&mut self) {
283    let q = self.as_u64_array_mut();
284    let n = q.len();
285    for i in &mut q[..n-1] {
286      *i = 0;
287    }
288    q[n - 1] = i64::MIN as u64
289  }
290}
291
292#[cfg(test)]
293mod tests {
294  use super::*;
295
296  /// Source: <https://posithub.org/docs/posit_standard-2.pdf#subsection.3.2>
297  #[test]
298  fn bits() {
299    assert_eq!(crate::q8::BITS, 128);
300    assert_eq!(crate::q16::BITS, 256);
301    assert_eq!(crate::q32::BITS, 512);
302    assert_eq!(crate::q64::BITS, 1024);
303  }
304
305  /// Source: <https://posithub.org/docs/posit_standard-2.pdf#subsection.3.2>
306  #[test]
307  fn size() {
308    assert_eq!(crate::q8::SIZE, 16);
309    assert_eq!(crate::q16::SIZE, 32);
310    assert_eq!(crate::q32::SIZE, 64);
311    assert_eq!(crate::q64::SIZE, 128);
312  }
313
314  /// Source: <https://posithub.org/docs/posit_standard-2.pdf#subsection.3.4>
315  #[test]
316  fn width() {
317    assert_eq!(crate::q8::WIDTH, 8 * 8  - 16);
318    assert_eq!(crate::q16::WIDTH, 8 * 16 - 16);
319    assert_eq!(crate::q32::WIDTH, 8 * 32 - 16);
320    assert_eq!(crate::q64::WIDTH, 8 * 64 - 16);
321  }
322
323  /// Source: <https://posithub.org/docs/posit_standard-2.pdf#subsection.3.2>
324  #[test]
325  fn min_size() {
326    assert_eq!(8 * crate::q8::MIN_SIZE, 96  + 8);
327    assert_eq!(8 * crate::q16::MIN_SIZE, 224 + 8);
328    assert_eq!(8 * crate::q32::MIN_SIZE, 480 + 8);
329    assert_eq!(8 * crate::q64::MIN_SIZE, 992 + 8);
330  }
331
332  /// With the above `MIN_SIZE`s, still compiles fine, but below that it doesn't (see
333  /// [tests_compile_fail]).
334  #[test]
335  fn min_size_compiles() {
336    let _ = Quire::<8,  2, {96 /8 + 1}>::SIZE;
337    let _ = Quire::<16, 2, {224/8 + 1}>::SIZE;
338    let _ = Quire::<32, 2, {480/8 + 1}>::SIZE;
339    let _ = Quire::<64, 2, {992/8 + 1}>::SIZE;
340  }
341
342  /// Source: <https://posithub.org/docs/posit_standard-2.pdf#subsection.3.2>
343  #[test]
344  fn sum_limit() {
345    assert_eq!(crate::q8::SUM_LIMIT, 55);
346    assert_eq!(crate::q16::SUM_LIMIT, 87);
347    assert_eq!(crate::q32::SUM_LIMIT, 151);
348    assert_eq!(crate::q64::SUM_LIMIT, 279);
349  }
350
351  /// Source: <https://posithub.org/docs/posit_standard-2.pdf#subsection.3.2>
352  #[test]
353  fn prod_limit() {
354    assert_eq!(crate::q8::PROD_LIMIT, 31);
355    assert_eq!(crate::q16::PROD_LIMIT, 31);
356    assert_eq!(crate::q32::PROD_LIMIT, 31);
357    assert_eq!(crate::q64::PROD_LIMIT, 31);
358  }
359
360  #[test]
361  fn is_nar() {
362    assert!(crate::q8::NAR.is_nar());
363    assert!(crate::q16::NAR.is_nar());
364    assert!(crate::q32::NAR.is_nar());
365    assert!(crate::q64::NAR.is_nar());
366  }
367
368  #[test]
369  fn is_nar_manual() {
370    let bits = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80];
371    assert!(crate::q8::from_le_bytes(bits).is_nar());
372    let bits = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x81];
373    assert!(!crate::q8::from_le_bytes(bits).is_nar());
374    let bits = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x42, 0, 0x80];
375    assert!(!crate::q8::from_le_bytes(bits).is_nar());
376    let bits = [0, 0, 0, 0x42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80];
377    assert!(!crate::q8::from_le_bytes(bits).is_nar());
378    let bits = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f];
379    assert!(!crate::q8::from_le_bytes(bits).is_nar());
380    let bits = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
381    assert!(!crate::q8::from_le_bytes(bits).is_nar());
382  }
383
384  #[test]
385  fn set_nar() {
386    let mut q = crate::q8::from_le_bytes([0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf]);
387    assert!(!q.is_nar());
388    q.set_nar();
389    assert!(q.is_nar());
390  }
391}
392
393mod tests_compile_fail {
394  // TODO fix sizes in the future when relaxing the "multiple of 64 bits" constraint
395
396  /// ```compile_fail
397  /// use fast_posit::Quire;
398  /// let mut q: Quire<8, 2, /*12*/ 8> = Quire::ZERO;
399  /// q += fast_posit::p8::ONE;
400  /// ```
401  #[allow(dead_code)]
402  fn quire_size_too_small_8() {}
403
404  /// ```compile_fail
405  /// use fast_posit::Quire;
406  /// let mut q: Quire<16, 2, /*28*/ 24> = Quire::ZERO;
407  /// q += fast_posit::p16::ONE;
408  /// ```
409  #[allow(dead_code)]
410  fn quire_size_too_small_16() {}
411
412  /// ```compile_fail
413  /// use fast_posit::Quire;
414  /// let mut q: Quire<32, 2, /*60*/ 56> = Quire::ZERO;
415  /// q += fast_posit::p32::ONE;
416  /// ```
417  #[allow(dead_code)]
418  fn quire_size_too_small_32() {}
419
420  /// ```compile_fail
421  /// use fast_posit::Quire;
422  /// let mut q: Quire<64, 2, /*124*/ 120> = Quire::ZERO;
423  /// q += fast_posit::p64::ONE;
424  /// ```
425  #[allow(dead_code)]
426  fn quire_size_too_small_64() {}
427}