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}