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