fast_posit/posit/
round_int.rs

1use super::*;
2
3impl<
4  const N: u32,
5  const ES: u32,
6  Int: crate::Int,
7> Posit<N, ES, Int> {
8  // TODO note that these functions can probably be made more efficient if we don't call
9  // encode/decode, since all we have to do is manipulate the `frac` field without touching the
10  // exponents (not quite, because of overflow, but that overflow of frac into exp and regime is
11  // handled elegantly due to the posit bit format).
12
13  /// Returns the integer-valued posit nearest to `self`, and the nearest even integer-valued posit
14  /// if two integers are equally near.
15  ///
16  /// Standard: "[**nearestInt**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.2)".
17  ///
18  /// # Example
19  ///
20  /// ```
21  /// # use fast_posit::*;
22  /// assert_eq!(p32::round_from(3.1).nearest_int(), p32::round_from(3));
23  /// assert_eq!(p32::round_from(3.5).nearest_int(), p32::round_from(4));
24  /// assert_eq!(p32::round_from(3.9).nearest_int(), p32::round_from(4));
25  /// ```
26  pub fn nearest_int(self) -> Self {
27    if self.is_special() { return self }
28    // SAFETY: `self` is not 0 or NaR
29    let decoded = unsafe { self.decode_regular() };
30
31    // Let's split `decoded.frac` into an `integral` (left of the decimal dot) and `fractional`
32    // (right of the decimal dot) part. For an `exp` of 0, the decimal dot is `FRAC_WIDTH` places
33    // from the right / 2 places from the left. If the `exp` is bigger or smaller, the dot moves
34    // right or left respectively.
35    //
36    // Examples:
37    //
38    //         frac: 0b01_1101
39    //          exp: +0
40    //     integral: 0b01
41    //   fractional: 0b110100
42    //
43    //         frac: 0b01_1101
44    //          exp: +2
45    //     integral: 0b0111
46    //   fractional: 0b010000
47    //
48    //         frac: 0b01_1101
49    //          exp: -1
50    //     integral: 0b0
51    //   fractional: 0b101110
52    let integral_bits = (Int::ONE + Int::ONE).wrapping_add(decoded.exp);
53
54    // If there are no bits in the integral part, then the result is just 0.
55    //
56    //   - Positive case: 0b01_xxxx ×2^-2 = [+0.25,+0.50[ ⇒ rounds to 0
57    //   - Negative case: 0b10_xxxx ×2^-2 = [-0.50,-0.25[ ⇒ rounds to 0
58    if integral_bits <= Int::ZERO {
59      return Posit::ZERO
60    }
61    // If the are no bits in the fractional part, then the result is the posit itself, which is
62    // already an integer.
63    if integral_bits >= Int::of_u32(Int::BITS) {
64      return self;
65    }
66
67    // Otherwise, grab the integral and fractional part. The fractional part tells us whether we
68    // need to round the integral part up or not: we do round up if the fractional part is greater
69    // than 0.5, or if it is exactly 0.5 and the integral part is odd (so that we round to an even
70    // number).
71    let integral_bits = integral_bits.as_u32();
72    let fractional_bits = Int::BITS - integral_bits;
73    let integral = decoded.frac >> fractional_bits;
74    let fractional = decoded.frac << integral_bits;
75
76    // If `fractional` is `0b0xxx…`, then we round down. If it's `0b1xxx…`, then we round up,
77    // unless it's exactly `0b1000…`, in which case we round up if `integral` is odd.
78    let round = !fractional.is_positive();
79    let sticky = fractional << 1 != Int::ZERO;
80    let odd = integral.get_lsb();
81    let round_up: bool = round & (odd | sticky);
82
83    // TODO This is an interesting alternative formulation of the round_up formula, maybe we can
84    // use it elsewhere too.
85    /*let round_up = !(fractional | Int::from(odd)).is_positive();*/
86
87    // Tricky detail! If we do round up, we might overflow to the next exponent (e.g. from 0b01_11
88    // to 0b01_000); if this happens then we must subtract 1 from `fractional_bits` and add 1 to
89    // `exp`. In the case of a negative `frac`, the reverse might happen. To handle both cases
90    // without branching, we turn to our trusty `leading_run_minus_one`.
91    let integral_rounded = integral + Int::from(round_up);
92    if integral_rounded == Int::ZERO {
93      return Posit::ZERO
94    }
95    // SAFETY: `integral_rounded` is not 0 (checked) nor Int::MIN (impossible)
96    let true_fractional_bits = unsafe { integral_rounded.leading_run_minus_one() };
97    let frac = integral_rounded << true_fractional_bits;
98    let exp = decoded.exp + (Int::of_u32(fractional_bits) - Int::of_u32(true_fractional_bits));
99
100    // SAFETY: `frac` can only be underflowing if it's 0, which we checked above.
101    unsafe { Decoded { frac, exp }.encode_regular() }
102  }
103
104  /// Returns the largest integer-valued posit less than or equal to `self`.
105  ///
106  /// Standard: "[**floor**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.2)".
107  ///
108  /// # Example
109  ///
110  /// ```
111  /// # use fast_posit::*;
112  /// assert_eq!(p32::round_from(3.1).floor(), p32::round_from(3));
113  /// assert_eq!(p32::round_from(3.5).floor(), p32::round_from(3));
114  /// assert_eq!(p32::round_from(3.9).floor(), p32::round_from(3));
115  /// ```
116  pub fn floor(self) -> Self {
117    // `floor` follows the same template as `nearest_int`, only it is considerably simpler because
118    // we don't care about rounding. For details, see the comments in the code of [`nearest_int`].
119
120    if self.is_special() { return self }
121    // SAFETY: `self` is not 0 or NaR
122    let decoded = unsafe { self.decode_regular() };
123
124    let integral_bits = (Int::ONE + Int::ONE).wrapping_add(decoded.exp);
125
126    // If there are no bits in the integral part, then the result is 0 or -1.
127    //
128    //   - Positive case: 0b01_xxxx ×2^-2 = [+0.25,+0.50[ ⇒ rounds to 0
129    //   - Negative case: 0b10_xxxx ×2^-2 = [-0.50,-0.25[ ⇒ rounds to -1
130    if integral_bits <= Int::ZERO {
131      return if self >= Posit::ZERO {Posit::ZERO} else {Posit::MINUS_ONE}
132    }
133    // If the are no bits in the fractional part, then the result is the posit itself, which is
134    // already an integer.
135    if integral_bits >= Int::of_u32(Int::BITS) {
136      return self;
137    }
138
139    let integral_bits = integral_bits.as_u32();
140    let frac = decoded.frac.mask_msb(integral_bits);
141    let exp = decoded.exp;
142    if frac == Int::ZERO {
143      return Posit::ZERO
144    }
145
146    // SAFETY: `frac` can only be underflowing if it's 0, which we checked in the other branch.
147    unsafe { Decoded { frac, exp }.encode_regular() }
148  }
149
150  /// Returns the smallest integer-valued posit greater than or equal to `self`.
151  ///
152  /// Standard: "[**ceil**](https://posithub.org/docs/posit_standard-2.pdf#subsection.5.2)".
153  ///
154  /// # Example
155  ///
156  /// ```
157  /// # use fast_posit::*;
158  /// assert_eq!(p32::round_from(3.1).ceil(), p32::round_from(4));
159  /// assert_eq!(p32::round_from(3.5).ceil(), p32::round_from(4));
160  /// assert_eq!(p32::round_from(3.9).ceil(), p32::round_from(4));
161  /// ```
162  pub fn ceil(self) -> Self {
163    // `floor` follows the same template as `nearest_int`. Once again we care about rounding, but
164    // the rule is considerably simpler: round up if `fractional != 0`. For details, see the
165    // comments in the code of [`nearest_int`].
166
167    if self.is_special() { return self }
168    // SAFETY: `self` is not 0 or NaR
169    let decoded = unsafe { self.decode_regular() };
170
171    let integral_bits = (Int::ONE + Int::ONE).wrapping_add(decoded.exp);
172    // If there are no bits in the integral part, then the result is 0 or +1.
173    //
174    //   - Positive case: 0b01_xxxx ×2^-2 = [+0.25,+0.50[ ⇒ rounds to 1
175    //   - Negative case: 0b10_xxxx ×2^-2 = [-0.50,-0.25[ ⇒ rounds to 0
176    if integral_bits <= Int::ZERO {
177      return if self >= Posit::ZERO {Posit::ONE} else {Posit::ZERO}
178    }
179    // If the are no bits in the fractional part, then the result is the posit itself, which is
180    // already an integer.
181    if integral_bits >= Int::of_u32(Int::BITS) {
182      return self;
183    }
184
185    let integral_bits = integral_bits.as_u32();
186    let fractional_bits = Int::BITS - integral_bits;
187    let integral = decoded.frac >> fractional_bits;
188    let fractional = decoded.frac << integral_bits;
189
190    let round_up: bool = fractional != Int::ZERO;
191
192    let integral_rounded = integral + Int::from(round_up);
193    if integral_rounded == Int::ZERO {
194      return Posit::ZERO
195    }
196    // SAFETY: `integral_rounded` is not 0 (checked) nor Int::MIN (impossible)
197    let true_fractional_bits = unsafe { integral_rounded.leading_run_minus_one() };
198    let frac = integral_rounded << true_fractional_bits;
199    let exp = decoded.exp + (Int::of_u32(fractional_bits) - Int::of_u32(true_fractional_bits));
200
201    // SAFETY: `frac` can only be underflowing if it's 0, which we checked in the other branch.
202    unsafe { Decoded { frac, exp }.encode_regular() }
203  }
204}
205
206#[cfg(test)]
207mod tests {
208  use super::*;
209  use malachite::rational::Rational;
210  use proptest::prelude::*;
211
212  use malachite::base::rounding_modes::RoundingMode;
213
214  /// Aux function: check that `posit` rounded to `rounded_posit` is correct for `rounding_mode`.
215  fn is_correct_rounded<const N: u32, const ES: u32, Int: crate::Int>(
216    posit: Posit<N, ES, Int>,
217    rounded_posit: Posit<N, ES, Int>,
218    rounding_mode: RoundingMode,
219  ) -> bool
220  where
221    Rational: From<i32> + TryFrom<Posit<N, ES, Int>, Error = super::rational::IsNaR>,
222  {
223    use malachite::base::num::arithmetic::traits::RoundToMultiple;
224    let posit = Rational::try_from(posit)
225      .map(|exact| exact.round_to_multiple(Rational::from(1), rounding_mode).0);
226    let rounded_posit = Rational::try_from(rounded_posit);
227    posit == rounded_posit
228  }
229
230  mod nearest_int {
231    use super::*;
232
233    macro_rules! test_exhaustive {
234      ($name:ident, $posit:ty) => {
235        #[test]
236        fn $name() {
237          for p in <$posit>::cases_exhaustive_all() {
238            let rounded = p.nearest_int();
239            assert!(is_correct_rounded(p, rounded, RoundingMode::Nearest), "{p:?} {rounded:?}")
240          }
241        }
242      };
243    }
244
245    macro_rules! test_proptest {
246      ($name:ident, $posit:ty) => {
247        proptest!{
248          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES))]
249          #[test]
250          fn $name(p in <$posit>::cases_proptest_all()) {
251            let rounded = p.nearest_int();
252            assert!(is_correct_rounded(p, rounded, RoundingMode::Nearest), "{p:?} {rounded:?}")
253          }
254        }
255      };
256    }
257
258    test_exhaustive!{posit_10_0_exhaustive, Posit<10, 0, i16>}
259    test_exhaustive!{posit_10_1_exhaustive, Posit<10, 1, i16>}
260    test_exhaustive!{posit_10_2_exhaustive, Posit<10, 2, i16>}
261    test_exhaustive!{posit_10_3_exhaustive, Posit<10, 3, i16>}
262
263    test_exhaustive!{posit_8_0_exhaustive,  Posit<8,  0, i8 >}
264
265    test_exhaustive!{p8_exhaustive, crate::p8}
266    test_exhaustive!{p16_exhaustive, crate::p16}
267    test_proptest!{p32_proptest, crate::p32}
268    test_proptest!{p64_proptest, crate::p64}
269
270    test_exhaustive!{posit_3_0_exhaustive, Posit::<3, 0, i8>}
271    test_exhaustive!{posit_4_0_exhaustive, Posit::<4, 0, i8>}
272    test_exhaustive!{posit_4_1_exhaustive, Posit::<4, 1, i8>}
273  }
274
275  mod floor {
276    use super::*;
277
278    macro_rules! test_exhaustive {
279      ($name:ident, $posit:ty) => {
280        #[test]
281        fn $name() {
282          for p in <$posit>::cases_exhaustive_all() {
283            let rounded = p.floor();
284            assert!(is_correct_rounded(p, rounded, RoundingMode::Floor), "{p:?} {rounded:?}")
285          }
286        }
287      };
288    }
289
290    macro_rules! test_proptest {
291      ($name:ident, $posit:ty) => {
292        proptest!{
293          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES))]
294          #[test]
295          fn $name(p in <$posit>::cases_proptest_all()) {
296            let rounded = p.floor();
297            assert!(is_correct_rounded(p, rounded, RoundingMode::Floor), "{p:?} {rounded:?}")
298          }
299        }
300      };
301    }
302
303    test_exhaustive!{posit_10_0_exhaustive, Posit<10, 0, i16>}
304    test_exhaustive!{posit_10_1_exhaustive, Posit<10, 1, i16>}
305    test_exhaustive!{posit_10_2_exhaustive, Posit<10, 2, i16>}
306    test_exhaustive!{posit_10_3_exhaustive, Posit<10, 3, i16>}
307
308    test_exhaustive!{posit_8_0_exhaustive,  Posit<8,  0, i8 >}
309
310    test_exhaustive!{p8_exhaustive, crate::p8}
311    test_exhaustive!{p16_exhaustive, crate::p16}
312    test_proptest!{p32_proptest, crate::p32}
313    test_proptest!{p64_proptest, crate::p64}
314
315    test_exhaustive!{posit_3_0_exhaustive, Posit::<3, 0, i8>}
316    test_exhaustive!{posit_4_0_exhaustive, Posit::<4, 0, i8>}
317    test_exhaustive!{posit_4_1_exhaustive, Posit::<4, 1, i8>}
318  }
319
320  mod ceil {
321    use super::*;
322
323    macro_rules! test_exhaustive {
324      ($name:ident, $posit:ty) => {
325        #[test]
326        fn $name() {
327          for p in <$posit>::cases_exhaustive_all() {
328            let rounded = p.ceil();
329            assert!(is_correct_rounded(p, rounded, RoundingMode::Ceiling), "{p:?} {rounded:?}")
330          }
331        }
332      };
333    }
334
335    macro_rules! test_proptest {
336      ($name:ident, $posit:ty) => {
337        proptest!{
338          #![proptest_config(ProptestConfig::with_cases(crate::PROPTEST_CASES))]
339          #[test]
340          fn $name(p in <$posit>::cases_proptest_all()) {
341            let rounded = p.ceil();
342            assert!(is_correct_rounded(p, rounded, RoundingMode::Ceiling), "{p:?} {rounded:?}")
343          }
344        }
345      };
346    }
347
348    test_exhaustive!{posit_10_0_exhaustive, Posit<10, 0, i16>}
349    test_exhaustive!{posit_10_1_exhaustive, Posit<10, 1, i16>}
350    test_exhaustive!{posit_10_2_exhaustive, Posit<10, 2, i16>}
351    test_exhaustive!{posit_10_3_exhaustive, Posit<10, 3, i16>}
352
353    test_exhaustive!{posit_8_0_exhaustive,  Posit<8,  0, i8 >}
354
355    test_exhaustive!{p8_exhaustive, crate::p8}
356    test_exhaustive!{p16_exhaustive, crate::p16}
357    test_proptest!{p32_proptest, crate::p32}
358    test_proptest!{p64_proptest, crate::p64}
359
360    test_exhaustive!{posit_3_0_exhaustive, Posit::<3, 0, i8>}
361    test_exhaustive!{posit_4_0_exhaustive, Posit::<4, 0, i8>}
362    test_exhaustive!{posit_4_1_exhaustive, Posit::<4, 1, i8>}
363  }
364}