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}