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