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: &[i64] = self.as_int_array();
12    let leading = quire[0];
13    match leading {
14      0 => {
15        for i in 1 .. quire.len() {
16          if quire[i] != 0 {
17            return i as u32 * 64 + quire[i].to_be().leading_zeros()
18          }
19        }
20        return quire.len() as u32 * 64
21      },
22      -1 => {
23        for i in 1 .. quire.len() {
24          if quire[i] != -1 {
25            return i as u32 * 64 + quire[i].to_be().leading_ones()
26          }
27        }
28        return quire.len() as u32 * 64
29      },
30      _ => {
31        if leading << 1 == 0 {
32          1
33        } else {
34          use crate::underlying::Sealed;
35          unsafe { leading.to_be().leading_run_minus_one() + 1 }
36        }
37      },
38    }
39  }
40}
41
42impl<
43  const N: u32,
44  const ES: u32,
45  Int: crate::Int,
46  const SIZE: usize,
47> RoundFrom<&'_ Quire<N, ES, SIZE>> for Posit<N, ES, Int> {
48  /// Round a quire back to a posit. This is the final step to do after a series of calculations in
49  /// the quire, and the *only* step that actually rounds.
50  ///
51  /// Standard: "[**qToP**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
52  ///
53  /// # Example
54  ///
55  /// ```
56  /// # use fast_posit::*;
57  /// let mut quire = q16::from(p16::MAX);
58  /// quire += p16::round_from(0.1);
59  /// quire -= p16::MAX;
60  /// let result: p16 = (&quire).round_into();
61  /// assert_eq!(result, p16::round_from(0.1))
62  /// ```
63  fn round_from(value: &'_ Quire<N, ES, SIZE>) -> Self {
64    // Find the number of leading 0s or 1s in the quire. This is what will determine the `exp`, as
65    // well as the position of the `frac` bits.
66    let leading = value.leading_run();
67    // If the quire is NaR, the posit to be returned is NaR, of course.
68    if leading == 1 && value.is_nar() { return Posit::NAR }
69
70    // The first 1 after the 0s, or the first 0 after the 1s, is `leading` places from the left of
71    // the quire. After that, the `Int::BITS` bits that follow are the `frac` of our result. After
72    // that, the remaining bits until the end of the quire are the `sticky` bits.
73    //
74    // Visualised, for an example 8-bit posit:
75    //
76    //   quire: 1111111111111111111111111|10110101|001110101110101
77    //          leading                   frac     sticky
78    //
79    let leading_ints = leading / Int::BITS;
80    let leading_bits = leading % Int::BITS;
81    let quire: &[Int] = value.as_int_array();
82
83    // First, the exponent. The "decimal point" of the quire is `BITS - leading - 1` bits from the
84    // right, so the difference between that and `WIDTH` is the exponent.
85    //
86    // There's also a corner case for very large quires: if `BITS - leading` is large enough and
87    // `Int` is small enough, the `exp` may overflow the `Int`, so we need to early-return before
88    // any casts to `Int`. To ensure this does not introduce a branch where it's not needed
89    // (i.e. where there's no risk of any `exp` overflowing), we keep that behind an `if const`.
90    let value_width = Quire::<N, ES, SIZE>::BITS - leading;
91    if const { Quire::<N, ES, SIZE>::PROD_LIMIT >= 64 } {
92      if value_width > 2 * Quire::<N, ES, SIZE>::WIDTH + 1 {
93        return if quire[0].to_be().is_positive() {Posit::MAX} else {Posit::MIN}
94      }
95    }
96    let exp = Int::of_u32(value_width) - Int::of_u32(Quire::<N, ES, SIZE>::WIDTH) - Int::ONE;
97
98    // Next, the `frac`. As illustrated above, the `frac` starts after `leading` bits from the left
99    // and goes for `Int::BITS`. How we actually wrangle that into one byte depends on the
100    // following two cases. To illustrate, assume `Int` is `i8`.
101    //
102    // - `leading_bits == 0`: Then `frac` is `quire[leading_ints]` shifted 1 place to the right.
103    //   The msb of `frac` should be the msb of `quire[leading_ints - 1]`. The lsb of `quire
104    //   [leading_ints]` should be accumulated in the `sticky`.
105    //
106    //   00000000|11000101|10111011|…
107    //          [  frac ][sticky
108    //
109    // - `leading_bits != 0`: Then `frac` is `quire[leading_ints]` shifted `leading_bits - 1`
110    //   places to the left. The lowest `leading_bits - 1` bits of `frac` should be the highest
111    //   `leading_bits - 1` bits of `quire[leading_ints + 1]`.
112    //
113    //   00000000|00001100|01011011|…
114    //               [  frac ][sticky
115    //
116    // Besides this, we just need to be careful about a couple of "early return" points, so that we
117    // never go out of bounds of the quire.
118
119    // TODO very branchy! Also too many endianness conversions! Revisit later to improve perf
120    let frac =
121      if leading_bits == 0 {
122        // Edge case: if `leading == Quire::SIZE` (i.e. `leading_ints == quire.len()`) then the
123        // result can only be 0 or MAX_NEGATIVE.
124        if leading_ints as usize == quire.len() {
125          // SAFETY: if `leading_ints == quire.len()`, then `quire[0]` has to be 0 or -1, which
126          // maps to the bits for `Posit::ZERO` and `Posit::MAX_NEGATIVE`.
127          return unsafe { Posit::from_bits_unchecked(quire[0]) }
128        }
129        let frac_hi = quire[leading_ints as usize - 1] << (Int::BITS - 1);
130        let frac_lo = quire[leading_ints as usize].to_be().lshr(1);
131        frac_hi | frac_lo
132      } else {
133        // Edge case: if `leading_ints` is `quire.len() - 1`, then we also need to stop here; it's
134        // as if `quire[leading_ints + 1]` was `0`.
135        let frac_hi = quire[leading_ints as usize].to_be() << (leading_bits - 1);
136        if leading_ints as usize == quire.len() - 1 {
137          // SAFETY: `frac` starts with only one leading 0 or 1, and `exp` is in bounds.
138          return unsafe { Decoded{frac: frac_hi, exp}.encode_regular() }
139        }
140        let frac_lo = if leading_bits == 1 {Int::ZERO} else {quire[leading_ints as usize + 1].to_be().lshr(Int::BITS - leading_bits + 1)};
141        frac_hi | frac_lo
142      };
143    let sticky = {
144      if leading_ints as usize == quire.len() - 1 {
145        Int::ZERO
146      } else {
147        let sticky_hi = quire[leading_ints as usize + 1].to_be() << leading_bits;
148        let mut sticky_lo = Int::ZERO;
149        for &i in &quire[leading_ints as usize + 1 ..] {
150          sticky_lo |= i
151        }
152        sticky_hi | sticky_lo | (quire[leading_ints as usize].to_be() & Int::from(leading_bits == 0))
153      }
154    };
155
156    // SAFETY: `frac` starts with only one leading 0 or 1, and `exp` is in bounds.
157    unsafe { Decoded{frac, exp}.encode_regular_round(sticky) }
158  }
159}
160
161// TODO should we have this alias? :\
162/*impl<
163  const N: u32,
164  const ES: u32,
165  Int: crate::Int,
166  const SIZE: usize,
167> RoundFrom<Quire<N, ES, SIZE>> for Posit<N, ES, Int> {
168  /// Round a quire back to a posit. This is the final step to do after a series of calculations in
169  /// the quire, and the *only* step that actually rounds.
170  ///
171  /// Standard: "[**qToP**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
172  fn round_from(value: Quire<N, ES, SIZE>) -> Self {
173    Self::round_from(&value)
174  }
175}*/
176
177impl<
178  const N: u32,
179  const ES: u32,
180  Int: crate::Int,
181  const SIZE: usize,
182> From<Posit<N, ES, Int>> for Quire<N, ES, SIZE> {
183  /// Create a quire from a posit value.
184  ///
185  /// Standard: "[**pToQ**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.11)".
186  ///
187  /// # Example
188  ///
189  /// ```
190  /// # use fast_posit::*;
191  /// let posit = p16::round_from(123);
192  /// let quire = q16::from(posit);
193  /// ```
194  fn from(value: Posit<N, ES, Int>) -> Self {
195    if value == Posit::ZERO {
196      Self::ZERO
197    } else if value == Posit::NAR {
198      Self::NAR
199    } else {
200      let mut quire = Self::ZERO;
201      // SAFETY: `value` is not 0 or NaR
202      let decoded = unsafe { value.decode_regular() };
203      // SAFETY: `decoded` comes from `Posit::decode_regular`, therefore its `exp` is in bounds
204      unsafe { quire.accumulate_decoded(decoded) };
205      quire
206    }
207  }
208}
209
210#[cfg(test)]
211mod tests {
212  use super::*;
213  use malachite::rational::Rational;
214  use proptest::prelude::*;
215
216  mod from_posit {
217    use super::*;
218
219    macro_rules! test_exhaustive {
220      ($name:ident, $posit:ty, $quire:ty) => {
221        #[test]
222        fn $name() {
223          for a in <$posit>::cases_exhaustive_all() {
224            assert_eq!(Rational::try_from(a), Rational::try_from(<$quire>::from(a)))
225          }
226        }
227      };
228    }
229
230    macro_rules! test_proptest {
231      ($name:ident, $posit:ty, $quire:ty) => {
232        proptest!{
233          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES))]
234          #[test]
235          fn $name(a in <$posit>::cases_proptest_all()) {
236            assert_eq!(Rational::try_from(a), Rational::try_from(<$quire>::from(a)))
237          }
238        }
239      };
240    }
241
242    test_exhaustive!{posit_10_0_exhaustive, Posit::<10, 0, i16>, Quire::<10, 0, 128>}
243    test_exhaustive!{posit_10_1_exhaustive, Posit::<10, 1, i16>, Quire::<10, 1, 128>}
244    test_exhaustive!{posit_10_2_exhaustive, Posit::<10, 2, i16>, Quire::<10, 2, 128>}
245    test_exhaustive!{posit_10_3_exhaustive, Posit::<10, 3, i16>, Quire::<10, 3, 128>}
246    test_exhaustive!{posit_8_0_exhaustive, Posit::<8, 0, i8>, Quire::<8, 0, 128>}
247
248    test_exhaustive!{p8_exhaustive, crate::p8, crate::q8}
249    test_exhaustive!{p16_exhaustive, crate::p16, crate::q16}
250    test_proptest!{p32_proptest, crate::p32, crate::q32}
251    test_proptest!{p64_proptest, crate::p64, crate::q64}
252
253    test_exhaustive!{posit_3_0_exhaustive, Posit::<3, 0, i8>, Quire::<3, 0, 128>}
254    test_exhaustive!{posit_4_0_exhaustive, Posit::<4, 0, i8>, Quire::<4, 0, 128>}
255    test_exhaustive!{posit_4_1_exhaustive, Posit::<4, 1, i8>, Quire::<4, 1, 128>}
256  }
257
258  mod from_quire {
259    use super::*;
260
261    macro_rules! test_proptest {
262      ($name:ident, $posit:ty, $quire:ty) => {
263        proptest!{
264          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES))]
265          #[test]
266          fn $name(q in <$quire>::cases_proptest_all()) {
267            let posit = <$posit>::round_from(&q);
268            let exact = Rational::try_from(q);
269            assert!(super::rational::try_is_correct_rounded(exact, posit))
270          }
271        }
272      };
273    }
274
275    test_proptest!{posit_10_0_proptest, Posit::<10, 0, i16>, Quire::<10, 0, 128>}
276    test_proptest!{posit_10_1_proptest, Posit::<10, 1, i16>, Quire::<10, 1, 128>}
277    test_proptest!{posit_10_2_proptest, Posit::<10, 2, i16>, Quire::<10, 2, 128>}
278    test_proptest!{posit_10_3_proptest, Posit::<10, 3, i16>, Quire::<10, 3, 128>}
279    test_proptest!{posit_8_0_proptest, Posit::<8, 0, i8>, Quire::<8, 0, 128>}
280
281    test_proptest!{p8_proptest, crate::p8, crate::q8}
282    test_proptest!{p16_proptest, crate::p16, crate::q16}
283    test_proptest!{p32_proptest, crate::p32, crate::q32}
284    test_proptest!{p64_proptest, crate::p64, crate::q64}
285
286    test_proptest!{posit_3_0_proptest, Posit::<3, 0, i8>, Quire::<3, 0, 128>}
287    test_proptest!{posit_4_0_proptest, Posit::<4, 0, i8>, Quire::<4, 0, 128>}
288    test_proptest!{posit_4_1_proptest, Posit::<4, 1, i8>, Quire::<4, 1, 128>}
289  }
290
291  mod roundtrip {
292    use super::*;
293
294    macro_rules! test_exhaustive {
295      ($name:ident, $posit:ty, $quire:ty) => {
296        #[test]
297        fn $name() {
298          for p in <$posit>::cases_exhaustive_all() {
299            assert_eq!(p, <$posit>::round_from(&<$quire>::from(p)))
300          }
301        }
302      };
303    }
304
305    macro_rules! test_proptest {
306      ($name:ident, $posit:ty, $quire:ty) => {
307        proptest!{
308          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES))]
309          #[test]
310          fn $name(p in <$posit>::cases_proptest_all()) {
311            assert_eq!(p, <$posit>::round_from(&<$quire>::from(p)))
312          }
313        }
314      };
315    }
316
317    test_exhaustive!{posit_10_0_exhaustive, Posit::<10, 0, i16>, Quire::<10, 0, 128>}
318    test_exhaustive!{posit_10_1_exhaustive, Posit::<10, 1, i16>, Quire::<10, 1, 128>}
319    test_exhaustive!{posit_10_2_exhaustive, Posit::<10, 2, i16>, Quire::<10, 2, 128>}
320    test_exhaustive!{posit_10_3_exhaustive, Posit::<10, 3, i16>, Quire::<10, 3, 128>}
321    test_exhaustive!{posit_8_0_exhaustive, Posit::<8, 0, i8>, Quire::<8, 0, 128>}
322
323    test_exhaustive!{p8_exhaustive, crate::p8, crate::q8}
324    test_exhaustive!{p16_exhaustive, crate::p16, crate::q16}
325    test_proptest!{p32_proptest, crate::p32, crate::q32}
326    test_proptest!{p64_proptest, crate::p64, crate::q64}
327
328    test_exhaustive!{posit_3_0_exhaustive, Posit::<3, 0, i8>, Quire::<3, 0, 128>}
329    test_exhaustive!{posit_4_0_exhaustive, Posit::<4, 0, i8>, Quire::<4, 0, 128>}
330    test_exhaustive!{posit_4_1_exhaustive, Posit::<4, 1, i8>, Quire::<4, 1, 128>}
331  }
332}