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.
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, in big endian order.
33  ///
34  /// # Example
35  ///
36  /// ```
37  /// # use fast_posit::*;
38  /// let quire = q8::from_bits([0,0,0,0, 0,0,0,0,0,1, 0,0,0,0,0,0]);
39  /// assert_eq!(p8::round_from(&quire), p8::ONE);
40  /// ```
41  pub const fn from_bits(bytes: [u8; SIZE]) -> Self {
42    Self(bytes)
43  }
44
45  /// Access the storage as an array of **big-endian** `Int`s.
46  ///
47  /// Limitation: even though the return size is known, we cannot return an `&[Int; N]` due to
48  /// limitations in the Rust type system. We have to hope that the compiler will inline and fold
49  /// the slice len :)
50  #[inline]
51  pub(crate) const fn as_int_array<Int: crate::Int>(&self) -> &[Int] {
52    const { assert!(SIZE % 8 == 0, "Quire SIZE must be a multiple of 64 bits (8 bytes)"); }
53    let ptr = self.0.as_ptr() as *const Int;
54    let len = SIZE / (Int::BITS as usize / 8);
55    // SAFETY: ptr and len form a valid slice; the size and alignment is correct, and any bit
56    // pattern is a valid u64 value.
57    unsafe { core::slice::from_raw_parts(ptr, len) }
58  }
59
60  /// Auxiliary const: the maximum (positive) exponent of a `Posit<N, ES, Int>`. The size of the
61  /// quire is directly related to this (see [`Self::MIN_SIZE`] and [`Self::WIDTH`] below).
62  const MAX_EXP: u32 = {
63    assert!(ES < 20, "Cannot use the quire with very high ES (≥ 20)");
64    let max_regime = N - 2;
65    max_regime << ES
66  };
67
68  /// The minimum size of a quire for `Posit<N, ES, Int>`.
69  ///
70  /// # Example
71  ///
72  /// ```
73  /// # use fast_posit::*;
74  /// assert_eq!(q16::MIN_SIZE, 29);
75  /// ```
76  pub const MIN_SIZE: usize = {
77    // At worst, need to be able to represent [Posit::MAX]² + [Posit::MIN]² as a fixed point
78    // number. So that's a number of bits equal to 2×2×max_exp. Then add 1 sign bit: that's the
79    // minimum quire size in bits.
80    let min_size_bits = 4 * Self::MAX_EXP + 1;
81    min_size_bits.div_ceil(8) as usize
82  };
83
84  /// The minimum number of operations on the quire that can lead to overflow is
85  /// 2 <sup>[`PROD_LIMIT`](Self::PROD_LIMIT)</sup>; any number of [`Self::add_prod`] calls
86  /// smaller than that is guaranteed not to overflow.
87  ///
88  /// # Example
89  ///
90  /// ```
91  /// # use fast_posit::*;
92  /// assert_eq!(q32::PROD_LIMIT, 31);  // Can do at least 2^31 - 1 products without overflow
93  /// ```
94  pub const PROD_LIMIT: u32 = {
95    let _ = Self::SIZE;
96    // The biggest possible product (Posit::MAX * Posit::MAX) takes `4 * MAX_EXP` bits. It can be
97    // accumulated `2 ^ M` times, where `M` is the difference between that and this quire's
98    // `SIZE`, before it overflows.
99    let min_size_bits = 4 * Self::MAX_EXP + 1;
100    8 * SIZE as u32 - min_size_bits
101  };
102
103  /// The minimum number of additions of posits that can lead to overflow is
104  /// 2 <sup>[`SUM_LIMIT`](Self::SUM_LIMIT)</sup>; any number of `+=` or `-=` operations smaller
105  /// than that is guaranteed not to overflow.
106  ///
107  /// # Example
108  ///
109  /// ```
110  /// # use fast_posit::*;
111  /// assert_eq!(q32::SUM_LIMIT, 151);  // Can sum at least 2^151 - 1 terms without overflow
112  /// ```
113  pub const SUM_LIMIT: u32 = {
114    let _ = Self::SIZE;
115    // The biggest possible posit value (Posit::MAX) takes `3 * MAX_EXP` bits. It can be
116    // accumulated `2 ^ M` times, where `M` is the difference between that and this quire's
117    // `SIZE`, before it overflows.
118    let min_size_bits = 3 * Self::MAX_EXP + 1;
119    8 * SIZE as u32 - min_size_bits
120  };
121
122  /// The position of the fixed point, that is: "1.0" is represented in the quire as `1 << WIDTH`.
123  pub(crate) const WIDTH: u32 = {
124    let _ = Self::SIZE;
125    2 * Self::MAX_EXP
126  };
127
128  // TODO assert this on operations with posits (cannot assert here because needs to take into
129  // account the posit's underlying Int):
130  // assert!(SIZE % N == 0, "The size of the quire type is not a multiple of the posit size");
131
132  /// A quire that represents the posit number 0.
133  pub const ZERO: Self = Self([0; SIZE]);
134
135  /// A quire that represents the posit value `NaR`.
136  pub const NAR: Self = {
137    let mut nar = Self::ZERO;
138    nar.0[0] = i8::MIN as u8;
139    nar
140  };
141
142  /// Checks whether `self` represents a NaR value.
143  ///
144  /// # Example
145  ///
146  /// ```
147  /// # use fast_posit::*;
148  /// assert!(q32::NAR.is_nar());
149  /// ```
150  pub const fn is_nar(&self) -> bool {
151    let _ = Self::NAR;
152    // This is more optimised than it looks. If the quire is not NaR, which is the "normal" and
153    // thus more important case to optimise, then most likely it will either start with
154    // `0b00…001…` (positive) of `0b11…110…` (negative), and thus return right away on the first
155    // if statement. This is because the only way it starts with `0b1000…` and yet is not NaR is
156    // if it's very very close to overflowing on the negative side; this is *exceedingly*
157    // unlikely.
158    //
159    // Therefore, for almost all cases where the quire is not NaR, we only need a compare and
160    // branch. Only on when the quire is NaR, or in the rare cases where it's not NaR but still
161    // starts with `0b1000…`, will we need to scan through the whole thing.
162    let quire: &[i64] = self.as_int_array();
163    if quire[0] != i64::MIN.to_be() { return false }  // TODO mark likely?
164    // Written in this awkward way because it's a `const fn`...
165    let mut i = 1;
166    while i < quire.len() {
167      if quire[i] != 0 { return false }
168      i += 1
169    }
170    true
171  }
172}
173
174#[cfg(test)]
175mod tests {
176  use super::*;
177
178  /// Source: <https://posithub.org/docs/posit_standard-2.pdf#subsection.3.2>
179  #[test]
180  fn bits() {
181    assert_eq!(crate::q8::BITS, 128);
182    assert_eq!(crate::q16::BITS, 256);
183    assert_eq!(crate::q32::BITS, 512);
184    assert_eq!(crate::q64::BITS, 1024);
185  }
186
187  /// Source: <https://posithub.org/docs/posit_standard-2.pdf#subsection.3.2>
188  #[test]
189  fn size() {
190    assert_eq!(crate::q8::SIZE, 16);
191    assert_eq!(crate::q16::SIZE, 32);
192    assert_eq!(crate::q32::SIZE, 64);
193    assert_eq!(crate::q64::SIZE, 128);
194  }
195
196  /// Source: <https://posithub.org/docs/posit_standard-2.pdf#subsection.3.4>
197  #[test]
198  fn width() {
199    assert_eq!(crate::q8::WIDTH, 8 * 8  - 16);
200    assert_eq!(crate::q16::WIDTH, 8 * 16 - 16);
201    assert_eq!(crate::q32::WIDTH, 8 * 32 - 16);
202    assert_eq!(crate::q64::WIDTH, 8 * 64 - 16);
203  }
204
205  /// Source: <https://posithub.org/docs/posit_standard-2.pdf#subsection.3.2>
206  #[test]
207  fn min_size() {
208    assert_eq!(8 * crate::q8::MIN_SIZE, 96  + 8);
209    assert_eq!(8 * crate::q16::MIN_SIZE, 224 + 8);
210    assert_eq!(8 * crate::q32::MIN_SIZE, 480 + 8);
211    assert_eq!(8 * crate::q64::MIN_SIZE, 992 + 8);
212  }
213
214  /// With the above `MIN_SIZE`s, still compiles fine, but below that it doesn't (see
215  /// [tests_compile_fail]).
216  #[test]
217  fn min_size_compiles() {
218    let _ = Quire::<8,  2, {96 /8 + 1}>::SIZE;
219    let _ = Quire::<16, 2, {224/8 + 1}>::SIZE;
220    let _ = Quire::<32, 2, {480/8 + 1}>::SIZE;
221    let _ = Quire::<64, 2, {992/8 + 1}>::SIZE;
222  }
223
224  /// Source: <https://posithub.org/docs/posit_standard-2.pdf#subsection.3.2>
225  #[test]
226  fn sum_limit() {
227    assert_eq!(crate::q8::SUM_LIMIT, 55);
228    assert_eq!(crate::q16::SUM_LIMIT, 87);
229    assert_eq!(crate::q32::SUM_LIMIT, 151);
230    assert_eq!(crate::q64::SUM_LIMIT, 279);
231  }
232
233  /// Source: <https://posithub.org/docs/posit_standard-2.pdf#subsection.3.2>
234  #[test]
235  fn prod_limit() {
236    assert_eq!(crate::q8::PROD_LIMIT, 31);
237    assert_eq!(crate::q16::PROD_LIMIT, 31);
238    assert_eq!(crate::q32::PROD_LIMIT, 31);
239    assert_eq!(crate::q64::PROD_LIMIT, 31);
240  }
241
242  #[test]
243  fn is_nar() {
244    assert!(crate::q8::NAR.is_nar());
245    assert!(crate::q16::NAR.is_nar());
246    assert!(crate::q32::NAR.is_nar());
247    assert!(crate::q64::NAR.is_nar());
248  }
249
250  #[test]
251  fn is_nar_manual() {
252    let bits = [0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
253    assert!(crate::q8::from_bits(bits).is_nar());
254    let bits = [0x81, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
255    assert!(!crate::q8::from_bits(bits).is_nar());
256    let bits = [0x80, 0, 0x42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
257    assert!(!crate::q8::from_bits(bits).is_nar());
258    let bits = [0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x42, 0, 0, 0];
259    assert!(!crate::q8::from_bits(bits).is_nar());
260    let bits = [0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
261    assert!(!crate::q8::from_bits(bits).is_nar());
262    let bits = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
263    assert!(!crate::q8::from_bits(bits).is_nar());
264  }
265}
266
267mod tests_compile_fail {
268  // TODO fix sizes in the future when relaxing the "multiple of 64 bits" constraint
269
270  /// ```compile_fail
271  /// use fast_posit::Quire;
272  /// let mut q: Quire<8, 2, /*12*/ 8> = Quire::ZERO;
273  /// q += fast_posit::p8::ONE;
274  /// ```
275  #[allow(dead_code)]
276  fn quire_size_too_small_8() {}
277
278  /// ```compile_fail
279  /// use fast_posit::Quire;
280  /// let mut q: Quire<16, 2, /*28*/ 24> = Quire::ZERO;
281  /// q += fast_posit::p16::ONE;
282  /// ```
283  #[allow(dead_code)]
284  fn quire_size_too_small_16() {}
285
286  /// ```compile_fail
287  /// use fast_posit::Quire;
288  /// let mut q: Quire<32, 2, /*60*/ 56> = Quire::ZERO;
289  /// q += fast_posit::p32::ONE;
290  /// ```
291  #[allow(dead_code)]
292  fn quire_size_too_small_32() {}
293
294  /// ```compile_fail
295  /// use fast_posit::Quire;
296  /// let mut q: Quire<64, 2, /*124*/ 120> = Quire::ZERO;
297  /// q += fast_posit::p64::ONE;
298  /// ```
299  #[allow(dead_code)]
300  fn quire_size_too_small_64() {}
301}