softposit/p16e1/math/
round.rs

1use super::P16E1;
2use crate::u16_with_sign;
3
4impl P16E1 {
5    pub const fn round(self) -> Self {
6        let mut mask = 0x2000_u16;
7        let mut scale = 0_u16;
8
9        let mut ui_a = self.to_bits();
10        let sign = ui_a > 0x8000;
11
12        // sign is True if p_a > NaR.
13        if sign {
14            ui_a = ui_a.wrapping_neg() // A is now |A|.
15        };
16        let u_a = if ui_a <= 0x3000 {
17            // 0 <= |p_a| <= 1/2 rounds to zero.
18            return Self::ZERO;
19        } else if ui_a < 0x4800 {
20            // 1/2 < x < 3/2 rounds to 1.
21            0x4000
22        } else if ui_a <= 0x5400 {
23            // 3/2 <= x <= 5/2 rounds to 2.
24            0x5000
25        } else if ui_a >= 0x7C00 {
26            // If |A| is 256 or greater, leave it unchanged.
27            return self; // This also takes care of the NaR case, 0x8000.
28        } else {
29            // 34% of the cases, we have to decode the posit.
30            while (mask & ui_a) != 0 {
31                // Increment scale by 2 for each regime sign bit.
32                scale += 2; // Regime sign bit is always 1 in this range.
33                mask >>= 1; // Move the mask right, to the next bit.
34            }
35            mask >>= 1; // Skip over termination bit.
36            if (mask & ui_a) != 0 {
37                scale += 1; // If exponent is 1, increment the scale.
38            }
39            mask >>= scale; // Point to the last bit of the integer part.
40            let bit_last = (ui_a & mask) != 0; // Extract the bit, without shifting it.
41
42            mask >>= 1;
43            let mut tmp = ui_a & mask;
44            let bit_n_plus_one = tmp != 0; // "True" if nonzero.
45            ui_a ^= tmp; // Erase the bit, if it was set.
46            tmp = ui_a & (mask - 1); // tmp has any remaining bits.
47            ui_a ^= tmp; // Erase those bits, if any were set.
48
49            if bit_n_plus_one {
50                // logic for round to nearest, tie to even
51                if (bit_last as u16 | tmp) != 0 {
52                    ui_a += mask << 1;
53                }
54            }
55            ui_a
56        };
57        Self::from_bits(u16_with_sign(u_a, sign))
58    }
59}
60
61#[test]
62fn test_round() {
63    use rand::Rng;
64    let mut rng = rand::thread_rng();
65    for _ in 0..crate::NTESTS16 {
66        let p_a: P16E1 = rng.gen();
67        let f_a = f64::from(p_a);
68        let p = p_a.round();
69        let f = f_a.round();
70        if (f - f_a).abs() == 0.5 {
71            continue;
72        }
73        assert_eq!(p, P16E1::from(f));
74    }
75}