Skip to main content

fast_posit/posit/quire/
convert.rs

1use super::*;
2use crate::RoundFrom;
3
4impl<
5  const N: u32,
6  const ES: u32,
7  const SIZE: usize,
8> Quire<N, ES, SIZE> {
9  /// Aux function: find number of leading 0s or 1s.
10  fn leading_run(&self) -> u32 {
11    let quire: &[u64] = self.as_u64_array();
12    let idx_last = quire.len() - 1;
13    match quire[idx_last] as i64 >= 0 {
14      true => {
15        for i in 0 .. quire.len() {
16          if quire[idx_last - i] != 0 {
17            return i as u32 * 64 + quire[idx_last - i].leading_zeros()
18          }
19        }
20        quire.len() as u32 * 64
21      },
22      false => {
23        for i in 0 .. quire.len() {
24          if quire[idx_last - i] != u64::MAX {
25            return i as u32 * 64 + quire[idx_last - i].leading_ones()
26          }
27        }
28        quire.len() as u32 * 64
29      },
30    }
31  }
32}
33
34impl<
35  const N: u32,
36  const ES: u32,
37  Int: crate::Int,
38  const SIZE: usize,
39> RoundFrom<&'_ Quire<N, ES, SIZE>> for Posit<N, ES, Int> {
40  /// Round a quire back to a posit. This is the final step to do after a series of calculations in
41  /// the quire, and the *only* step that actually rounds.
42  ///
43  /// Standard: "[**qToP**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
44  ///
45  /// # Example
46  ///
47  /// ```
48  /// # use fast_posit::*;
49  /// let mut quire = q16::from(p16::MAX);
50  /// quire += p16::round_from(0.1);
51  /// quire -= p16::MAX;
52  /// let result: p16 = (&quire).round_into();
53  /// assert_eq!(result, p16::round_from(0.1))
54  /// ```
55  fn round_from(value: &'_ Quire<N, ES, SIZE>) -> Self {
56    const { assert!(Int::BITS <= 64, "Only posits up to 64 bits are currently supported") };
57
58    // If the quire is NaR, the posit to be returned is NaR, of course.
59    if value.is_nar() { return Posit::NAR }
60    // Find the number of leading 0s or 1s in the quire, minus one. This is what will determine the
61    // `exp`, as well as the position of the `frac` bits.
62    let leading = value.leading_run() - 1;
63
64    // If `leading` is `Quire::BITS - 1`, then the quire is all 0s or all 1s. This maps to
65    // Posit::ZERO and POSIT::MIN, respectively, which are also all 0s or all 1s.
66    if leading + 1 == Quire::<N, ES, SIZE>::BITS {
67      // SAFETY: The size of `Quire` is greater than that of `Posit`.
68      return unsafe { core::mem::transmute_copy(value) }
69    }
70
71    // The first `01` or `10` sequence is `leading` places from the left of the quire (remember,
72    // "left" as in "most-significant", even though the bytes are stored little endian therefore
73    // the msb of the quire is at the end of the array). After that, the `Int::BITS` bits that
74    // follow are the `frac` of our result. After that, the remaining bits until the end of the
75    // quire are the `sticky` bits.
76    //
77    // Visualised, for an example 8-bit posit, with leading=25, and commas marking the 8-bit
78    // boundaries:
79    //
80    //   quire: 11111111,11111111,11111111,1|1011010,1|0011101,01110101
81    //          leading                   frac     sticky
82    //
83    let leading_ints = (leading / Int::BITS) as usize;
84    let leading_bits = (leading % Int::BITS) as u32;
85    let quire: &[Int] = value.as_int_array();
86    let idx_last = quire.len() - 1;
87
88    // First, the exponent. The "decimal point" of the quire is `BITS - leading - 2` bits from the
89    // right, so the difference between that and `WIDTH` is the exponent.
90    //
91    // There's also a corner case for very large quires: if `BITS - leading` is large enough and
92    // `Int` is small enough, the `exp` may overflow the `Int`, so we need to early-return before
93    // any casts to `Int`. To ensure this does not introduce a branch where it's not needed
94    // (i.e. where there's no risk of any `exp` overflowing), we keep that behind an `if const`.
95    let value_width = Quire::<N, ES, SIZE>::BITS - leading;
96    if const { Int::BITS <= 8 }
97    && value_width > 2 * Quire::<N, ES, SIZE>::WIDTH + 1 {
98      return if quire[idx_last].is_positive() {Posit::MAX} else {Posit::MIN}
99    }
100    let exp = Int::of_u32(value_width) - Int::of_u32(Quire::<N, ES, SIZE>::WIDTH) - Int::ONE - Int::ONE;
101
102    // Next, the `frac`. As illustrated above, the `frac` starts after `leading` bits from the left
103    // and goes for `Int::BITS`. How we actually wrangle that into one Int depends on the
104    // following two cases. To illustrate, assume `Int` is `i8`.
105    //
106    // - `leading_bits == 0`: Then `frac` is exactly `quire[leading_ints]`. Any further bytes to
107    //   the right should be accumulated in the `sticky`.
108    //
109    //   00000000|01100010|10111011|…    leading_ints = 1
110    //            [ frac ] [   sticky    leading_bits = 0
111    //
112    // - `leading_bits != 0`: Then `frac` is `quire[leading_ints]` shifted `leading_bits - 1`
113    //   places to the left. The lowest `leading_bits - 1` bits of `frac` should be the highest
114    //   `leading_bits - 1` bits of `quire[leading_ints + 1]`.
115    //
116    //   00000000|00001100|01011011|…     leading_ints = 1
117    //               [ frac  ][sticky     leading_bits = 3
118    //
119    // Besides this, we just need to be careful about a couple of edge cases, so that we never go
120    // out of bounds.
121    let frac = {
122      let frac_hi = quire[idx_last - leading_ints] << leading_bits;
123      // Edge case: if `leading_ints` is `idx_last`, then we also need to stop here; it's as if
124      // `quire[idx_last - leading_ints - 1]` was `0`.
125      let frac_lo = if leading_bits != 0 && leading_ints < idx_last {
126        quire[idx_last - leading_ints - 1].lshr(Int::BITS - leading_bits)
127      } else {
128        Int::ZERO
129      };
130      frac_hi | frac_lo
131    };
132    let sticky = {
133      // Again, if `leading_ints` is `idx_last`, then we must stop here.
134      let sticky_hi = if leading_ints < idx_last {
135        quire[idx_last - leading_ints - 1] << leading_bits
136      } else {
137        Int::ZERO
138      };
139      let mut sticky_lo = Int::ZERO;
140      if leading_ints < idx_last - 1 {
141        for &i in &quire[.. idx_last - leading_ints - 2] {
142          sticky_lo |= i
143        }
144      }
145      sticky_hi | sticky_lo
146    };
147
148    // SAFETY: `frac` starts with only one leading 0 or 1, and `exp` is in bounds.
149    unsafe { Decoded{frac, exp}.encode_regular_round(sticky) }
150  }
151}
152
153impl<
154  const N: u32,
155  const ES: u32,
156  Int: crate::Int,
157  const SIZE: usize,
158> RoundFrom<Quire<N, ES, SIZE>> for Posit<N, ES, Int> {
159  /// Round a quire back to a posit. This is the final step to do after a series of calculations in
160  /// the quire, and the *only* step that actually rounds.
161  ///
162  /// Standard: "[**qToP**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
163  ///
164  /// # Example
165  ///
166  /// ```
167  /// # use fast_posit::*;
168  /// let mut quire = q16::from(p16::MAX);
169  /// quire += p16::round_from(0.1);
170  /// quire -= p16::MAX;
171  /// let result: p16 = quire.round_into();
172  /// assert_eq!(result, p16::round_from(0.1))
173  /// ```
174  #[inline]
175  fn round_from(value: Quire<N, ES, SIZE>) -> Self {
176    Self::round_from(&value)
177  }
178}
179
180impl<
181  const N: u32,
182  const ES: u32,
183  Int: crate::Int,
184  const SIZE: usize,
185> From<Posit<N, ES, Int>> for Quire<N, ES, SIZE> {
186  /// Create a quire from a posit value.
187  ///
188  /// Standard: "[**pToQ**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
189  ///
190  /// # Example
191  ///
192  /// ```
193  /// # use fast_posit::*;
194  /// let posit = p16::round_from(123);
195  /// let quire = q16::from(posit);
196  /// ```
197  fn from(value: Posit<N, ES, Int>) -> Self {
198    if value == Posit::ZERO {
199      Self::ZERO
200    } else if value == Posit::NAR {
201      Self::NAR
202    } else {
203      let mut quire = Self::ZERO;
204      // SAFETY: `value` is not 0 or NaR
205      let decoded = unsafe { value.decode_regular() };
206      // SAFETY: `decoded` comes from `Posit::decode_regular`
207      unsafe { quire.add_posit_kernel(decoded) };
208      quire
209    }
210  }
211}
212
213#[cfg(test)]
214mod tests {
215  use super::*;
216  use malachite::rational::Rational;
217  use proptest::prelude::*;
218
219  #[test]
220  fn leading_run_3() {
221    let bytes = [
222      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
223      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
224      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
225      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0b00010000,
226    ];
227    assert_eq!(3, crate::q16::from_le_bytes(bytes).leading_run());
228    let bytes = [
229      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
230      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
231      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
232      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0b11100000,
233    ];
234    assert_eq!(3, crate::q16::from_le_bytes(bytes).leading_run());
235  }
236
237  #[test]
238  fn leading_run_1() {
239    let bytes = [
240      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
241      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
242      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
243      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0b01001101,
244    ];
245    assert_eq!(1, crate::q16::from_le_bytes(bytes).leading_run());
246    let bytes = [
247      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
248      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
249      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
250      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0b10000000,
251    ];
252    assert_eq!(1, crate::q16::from_le_bytes(bytes).leading_run());
253    assert_eq!(1, crate::q32::NAR.leading_run());
254  }
255
256  #[test]
257  fn leading_run_27() {
258    let bytes = [
259      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
260      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
261      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
262      0x00,0x11,0x22,0x33,0b00010111,0b00000000,0b00000000,0b00000000,
263    ];
264    assert_eq!(27, crate::q16::from_le_bytes(bytes).leading_run());
265    let bytes = [
266      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
267      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
268      0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,
269      0x00,0x11,0x22,0x33,0b11100111,0b11111111,0b11111111,0b11111111,
270    ];
271    assert_eq!(27, crate::q16::from_le_bytes(bytes).leading_run());
272  }
273
274  mod from_posit {
275    use super::*;
276
277    macro_rules! test_exhaustive {
278      ($name:ident, $posit:ty, $quire:ty) => {
279        #[test]
280        fn $name() {
281          for a in <$posit>::cases_exhaustive_all() {
282            assert_eq!(Rational::try_from(a), Rational::try_from(<$quire>::from(a)))
283          }
284        }
285      };
286    }
287
288    macro_rules! test_proptest {
289      ($name:ident, $posit:ty, $quire:ty) => {
290        proptest!{
291          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES))]
292          #[test]
293          fn $name(a in <$posit>::cases_proptest_all()) {
294            assert_eq!(Rational::try_from(a), Rational::try_from(<$quire>::from(a)))
295          }
296        }
297      };
298    }
299
300    test_exhaustive!{posit_10_0_exhaustive, Posit::<10, 0, i16>, Quire::<10, 0, 128>}
301    test_exhaustive!{posit_10_1_exhaustive, Posit::<10, 1, i16>, Quire::<10, 1, 128>}
302    test_exhaustive!{posit_10_2_exhaustive, Posit::<10, 2, i16>, Quire::<10, 2, 128>}
303    test_exhaustive!{posit_10_3_exhaustive, Posit::<10, 3, i16>, Quire::<10, 3, 128>}
304    test_exhaustive!{posit_8_0_exhaustive, Posit::<8, 0, i8>, Quire::<8, 0, 128>}
305
306    test_exhaustive!{p8_exhaustive, crate::p8, crate::q8}
307    test_exhaustive!{p16_exhaustive, crate::p16, crate::q16}
308    test_proptest!{p32_proptest, crate::p32, crate::q32}
309    test_proptest!{p64_proptest, crate::p64, crate::q64}
310
311    test_exhaustive!{posit_3_0_exhaustive, Posit::<3, 0, i8>, Quire::<3, 0, 128>}
312    test_exhaustive!{posit_4_0_exhaustive, Posit::<4, 0, i8>, Quire::<4, 0, 128>}
313    test_exhaustive!{posit_4_1_exhaustive, Posit::<4, 1, i8>, Quire::<4, 1, 128>}
314  }
315
316  mod from_quire {
317    use super::*;
318
319    macro_rules! test_proptest {
320      ($name:ident, $posit:ty, $quire:ty) => {
321        proptest!{
322          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES))]
323          #[test]
324          fn $name(q in <$quire>::cases_proptest_all()) {
325            let posit = <$posit>::round_from(&q);
326            let exact = Rational::try_from(q);
327            assert!(super::rational::try_is_correct_rounded(exact, posit))
328          }
329        }
330      };
331    }
332
333    test_proptest!{posit_10_0_proptest, Posit::<10, 0, i16>, Quire::<10, 0, 128>}
334    test_proptest!{posit_10_1_proptest, Posit::<10, 1, i16>, Quire::<10, 1, 128>}
335    test_proptest!{posit_10_2_proptest, Posit::<10, 2, i16>, Quire::<10, 2, 128>}
336    test_proptest!{posit_10_3_proptest, Posit::<10, 3, i16>, Quire::<10, 3, 128>}
337    test_proptest!{posit_8_0_proptest, Posit::<8, 0, i8>, Quire::<8, 0, 128>}
338
339    test_proptest!{p8_proptest, crate::p8, crate::q8}
340    test_proptest!{p16_proptest, crate::p16, crate::q16}
341    test_proptest!{p32_proptest, crate::p32, crate::q32}
342    test_proptest!{p64_proptest, crate::p64, crate::q64}
343
344    test_proptest!{posit_3_0_proptest, Posit::<3, 0, i8>, Quire::<3, 0, 128>}
345    test_proptest!{posit_4_0_proptest, Posit::<4, 0, i8>, Quire::<4, 0, 128>}
346    test_proptest!{posit_4_1_proptest, Posit::<4, 1, i8>, Quire::<4, 1, 128>}
347  }
348
349  mod roundtrip {
350    use super::*;
351
352    macro_rules! test_exhaustive {
353      ($name:ident, $posit:ty, $quire:ty) => {
354        #[test]
355        fn $name() {
356          for p in <$posit>::cases_exhaustive_all() {
357            assert_eq!(p, <$posit>::round_from(&<$quire>::from(p)))
358          }
359        }
360      };
361    }
362
363    macro_rules! test_proptest {
364      ($name:ident, $posit:ty, $quire:ty) => {
365        proptest!{
366          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES))]
367          #[test]
368          fn $name(p in <$posit>::cases_proptest_all()) {
369            assert_eq!(p, <$posit>::round_from(&<$quire>::from(p)))
370          }
371        }
372      };
373    }
374
375    test_exhaustive!{posit_10_0_exhaustive, Posit::<10, 0, i16>, Quire::<10, 0, 128>}
376    test_exhaustive!{posit_10_1_exhaustive, Posit::<10, 1, i16>, Quire::<10, 1, 128>}
377    test_exhaustive!{posit_10_2_exhaustive, Posit::<10, 2, i16>, Quire::<10, 2, 128>}
378    test_exhaustive!{posit_10_3_exhaustive, Posit::<10, 3, i16>, Quire::<10, 3, 128>}
379    test_exhaustive!{posit_8_0_exhaustive, Posit::<8, 0, i8>, Quire::<8, 0, 128>}
380
381    test_exhaustive!{p8_exhaustive, crate::p8, crate::q8}
382    test_exhaustive!{p16_exhaustive, crate::p16, crate::q16}
383    test_proptest!{p32_proptest, crate::p32, crate::q32}
384    test_proptest!{p64_proptest, crate::p64, crate::q64}
385
386    test_exhaustive!{posit_3_0_exhaustive, Posit::<3, 0, i8>, Quire::<3, 0, 128>}
387    test_exhaustive!{posit_4_0_exhaustive, Posit::<4, 0, i8>, Quire::<4, 0, 128>}
388    test_exhaustive!{posit_4_1_exhaustive, Posit::<4, 1, i8>, Quire::<4, 1, 128>}
389  }
390}