dashu_float/
round.rs

1//! Traits and implementations for rounding during operations.
2
3use core::cmp::Ordering;
4use core::ops::{Add, AddAssign};
5use dashu_base::{AbsOrd, Approximation, BitTest, EstimatedLog2, Sign, Signed, UnsignedAbs};
6use dashu_int::{IBig, UBig, Word};
7
8use crate::FBig;
9
10/// Built-in rounding modes of the floating numbers.
11///
12/// # Rounding Error
13///
14/// For different rounding modes, the [Rounding] error
15/// in the output of operations tells the error range, as described in
16/// the table below.
17///
18/// | Mode     | Rounding | Error (truth - estimation) Range |
19/// |----------|----------|----------------------------------|
20/// | Zero     | NoOp     | `(-1 ulp 0)` or `(0, 1 ulp)`*    |
21/// | Away     | AddOne   | `(-1 ulp, 0)`                    |
22/// | Away     | SubOne   | `(0, 1 ulp)`                     |
23/// | Down     | SubOne   | `(0, 1 ulp)`                     |
24/// | Up       | AddOne   | `(-1 ulp, 0)`                    |
25/// | HalfAway | AddOne   | `[-1/2 ulp, 0)`                  |
26/// | HalfAway | NoOp     | `(-1/2 ulp, 1/2 ulp)`            |
27/// | HalfAway | SubOne   | `(0, 1/2 ulp]`                   |
28/// | HalfEven | AddOne   | `[-1/2 ulp, 0)`                  |
29/// | HalfEven | NoOp     | `[-1/2 ulp, 1/2 ulp]`            |
30/// | HalfEven | SubOne   | `(0, 1/2 ulp]`                   |
31///
32/// *: Dependends on the sign of the result
33///
34pub mod mode {
35    /// Round toward 0 (default mode for binary float)
36    #[derive(Clone, Copy)]
37    pub struct Zero;
38
39    /// Round away from 0
40    #[derive(Clone, Copy)]
41    pub struct Away;
42
43    /// Round toward +∞
44    #[derive(Clone, Copy)]
45    pub struct Up;
46
47    /// Round toward -∞
48    #[derive(Clone, Copy)]
49    pub struct Down;
50
51    /// Round to the nearest value, ties are rounded to an even value. (default mode for decimal float)
52    #[derive(Clone, Copy)]
53    pub struct HalfEven;
54
55    /// Round to the nearest value, ties away from zero
56    #[derive(Clone, Copy)]
57    pub struct HalfAway;
58}
59
60/// The adjustment of a rounding operation
61///
62/// See [the `mode` module][mode] for the corresponding error bounds.
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum Rounding {
65    /// No adjustment
66    NoOp,
67
68    /// Add one
69    AddOne,
70
71    /// Subtract one
72    SubOne,
73}
74
75/// A type representing float operation result
76///
77/// If the operation result is inexact, the adjustment from the final rounding
78/// will be returned along with the result.
79pub type Rounded<T> = Approximation<T, Rounding>;
80
81/// A trait describing the rounding strategy
82pub trait Round: Copy {
83    /// The rounding operation that rounds to an opposite direction
84    type Reverse: Round;
85
86    /// Calculate the rounding of the number (integer + rem), assuming rem != 0 and |rem| < 1.
87    /// `low_half_test` should tell |rem|.cmp(0.5)
88    fn round_low_part<F: FnOnce() -> Ordering>(
89        integer: &IBig,
90        low_sign: Sign,
91        low_half_test: F,
92    ) -> Rounding;
93
94    /// Calculate the rounding of the number (integer + fract / X^precision),
95    /// assuming |fract| / X^precision < 1. Return the adjustment.
96    #[inline]
97    fn round_fract<const B: Word>(integer: &IBig, fract: IBig, precision: usize) -> Rounding {
98        // this assertion is costly, so only check in debug mode
99        debug_assert!(fract.clone().unsigned_abs() < UBig::from_word(B).pow(precision));
100
101        if fract.is_zero() {
102            return Rounding::NoOp;
103        }
104        let (fsign, fmag) = fract.into_parts();
105
106        let test = || {
107            // first use the estimated log2 to do coarse comparison, then do the exact comparison
108            let (lb, ub) = fmag.log2_bounds();
109            let (b_lb, b_ub) = B.log2_bounds();
110
111            // 0.999 and 1.001 are used here to prevent the influence of the precision loss of the multiplcations
112            if lb + 0.999 > b_ub * precision as f32 {
113                Ordering::Greater
114            } else if ub + 1.001 < b_lb * precision as f32 {
115                Ordering::Less
116            } else {
117                (fmag << 1).cmp(&UBig::from_word(B).pow(precision))
118            }
119        };
120        Self::round_low_part::<_>(integer, fsign, test)
121    }
122
123    /// Calculate the rounding of the number (integer + numerator / denominator),
124    /// assuming |numerator / denominator| < 1. Return the adjustment.
125    #[inline]
126    fn round_ratio(integer: &IBig, num: IBig, den: &IBig) -> Rounding {
127        assert!(!den.is_zero() && num.abs_cmp(den).is_le());
128
129        if num.is_zero() {
130            return Rounding::NoOp;
131        }
132        let (nsign, nmag) = num.into_parts();
133        Self::round_low_part::<_>(integer, nsign * den.sign(), || {
134            if den.is_positive() {
135                IBig::from(nmag << 1).cmp(den)
136            } else {
137                den.cmp(&-(nmag << 1))
138            }
139        })
140    }
141}
142
143/// A trait providing the function to retrieve the error bounds of the rounded value.
144pub trait ErrorBounds: Round {
145    /// Given a floating point number `f`, the output (L, R, incl_L, incl_R) represents the relative
146    /// error range with left bound `f - L` and right bound `f + R`. The two boolean values `incl_L`
147    /// and `incl_R` represents whether the bounds `f - L` and `f + R` are inclusive respectively.
148    ///
149    /// When the input number has unlimited precision, the output must be (ZERO, ZERO, true, true).
150    fn error_bounds<const B: Word>(f: &FBig<Self, B>)
151        -> (FBig<Self, B>, FBig<Self, B>, bool, bool);
152}
153
154impl Round for mode::Zero {
155    type Reverse = mode::Away;
156
157    #[inline]
158    fn round_low_part<F: FnOnce() -> Ordering>(
159        integer: &IBig,
160        low_sign: Sign,
161        _low_half_test: F,
162    ) -> Rounding {
163        if integer.is_zero() {
164            return Rounding::NoOp;
165        }
166        match (integer.sign(), low_sign) {
167            (Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => Rounding::NoOp,
168            (Sign::Positive, Sign::Negative) => Rounding::SubOne,
169            (Sign::Negative, Sign::Positive) => Rounding::AddOne,
170        }
171    }
172}
173
174impl ErrorBounds for mode::Zero {
175    #[inline]
176    fn error_bounds<const B: Word>(
177        f: &FBig<Self, B>,
178    ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
179        if f.precision() == 0 {
180            (FBig::ZERO, FBig::ZERO, true, true)
181        } else if f.repr().is_zero() {
182            (f.ulp(), f.ulp(), false, false)
183        } else {
184            match f.repr().sign() {
185                Sign::Positive => (FBig::ZERO, f.ulp(), true, false),
186                Sign::Negative => (f.ulp(), FBig::ZERO, false, true),
187            }
188        }
189    }
190}
191
192impl Round for mode::Away {
193    type Reverse = mode::Zero;
194
195    #[inline]
196    fn round_low_part<F: FnOnce() -> Ordering>(
197        integer: &IBig,
198        low_sign: Sign,
199        _low_half_test: F,
200    ) -> Rounding {
201        if integer.is_zero() {
202            match low_sign {
203                Sign::Positive => Rounding::AddOne,
204                Sign::Negative => Rounding::SubOne,
205            }
206        } else {
207            match (integer.sign(), low_sign) {
208                (Sign::Positive, Sign::Positive) => Rounding::AddOne,
209                (Sign::Negative, Sign::Negative) => Rounding::SubOne,
210                (Sign::Positive, Sign::Negative) | (Sign::Negative, Sign::Positive) => {
211                    Rounding::NoOp
212                }
213            }
214        }
215    }
216}
217
218impl ErrorBounds for mode::Away {
219    #[inline]
220    fn error_bounds<const B: Word>(
221        f: &FBig<Self, B>,
222    ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
223        if f.precision() == 0 && f.repr().is_zero() {
224            (FBig::ZERO, FBig::ZERO, true, true)
225        } else {
226            match f.repr().sign() {
227                Sign::Positive => (f.ulp(), FBig::ZERO, false, true),
228                Sign::Negative => (FBig::ZERO, f.ulp(), true, false),
229            }
230        }
231    }
232}
233
234impl Round for mode::Down {
235    type Reverse = mode::Up;
236
237    #[inline]
238    fn round_low_part<F: FnOnce() -> Ordering>(
239        _integer: &IBig,
240        low_sign: Sign,
241        _low_half_test: F,
242    ) -> Rounding {
243        // -1 if fract < 0, otherwise 0
244        if low_sign == Sign::Negative {
245            Rounding::SubOne
246        } else {
247            Rounding::NoOp
248        }
249    }
250}
251
252impl ErrorBounds for mode::Down {
253    #[inline]
254    fn error_bounds<const B: Word>(
255        f: &FBig<Self, B>,
256    ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
257        (FBig::ZERO, f.ulp(), true, false)
258    }
259}
260
261impl Round for mode::Up {
262    type Reverse = mode::Down;
263
264    #[inline]
265    fn round_low_part<F: FnOnce() -> Ordering>(
266        _integer: &IBig,
267        low_sign: Sign,
268        _low_half_test: F,
269    ) -> Rounding {
270        // +1 if fract > 0, otherwise 0
271        if low_sign == Sign::Positive {
272            Rounding::AddOne
273        } else {
274            Rounding::NoOp
275        }
276    }
277}
278
279impl ErrorBounds for mode::Up {
280    #[inline]
281    fn error_bounds<const B: Word>(
282        f: &FBig<Self, B>,
283    ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
284        (f.ulp(), FBig::ZERO, false, true)
285    }
286}
287
288impl Round for mode::HalfAway {
289    type Reverse = Self;
290
291    #[inline]
292    fn round_low_part<F: FnOnce() -> Ordering>(
293        integer: &IBig,
294        low_sign: Sign,
295        low_half_test: F,
296    ) -> Rounding {
297        match low_half_test() {
298            // |rem| < 1/2
299            Ordering::Less => Rounding::NoOp,
300            // |rem| = 1/2
301            Ordering::Equal => {
302                // +1 if integer and rem >= 0, -1 if integer and rem <= 0
303                if integer >= &IBig::ZERO && low_sign == Sign::Positive {
304                    Rounding::AddOne
305                } else if integer <= &IBig::ZERO && low_sign == Sign::Negative {
306                    Rounding::SubOne
307                } else {
308                    Rounding::NoOp
309                }
310            }
311            // |rem| > 1/2
312            Ordering::Greater => {
313                // +1 if rem > 0, -1 if rem < 0
314                match low_sign {
315                    Sign::Positive => Rounding::AddOne,
316                    Sign::Negative => Rounding::SubOne,
317                }
318            }
319        }
320    }
321}
322
323impl ErrorBounds for mode::HalfAway {
324    #[inline]
325    fn error_bounds<const B: Word>(
326        f: &FBig<Self, B>,
327    ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
328        if f.precision() == 0 {
329            return (FBig::ZERO, FBig::ZERO, true, true);
330        }
331
332        let mut half_ulp = f.ulp();
333        half_ulp.repr.exponent -= 1;
334        half_ulp.repr.significand = UBig::from_word((B + 1) / 2).into(); // ceil division
335
336        let (incl_l, incl_r) = if f.repr.is_zero() {
337            (false, false)
338        } else if f.repr.sign() == Sign::Negative {
339            (false, true)
340        } else {
341            (true, false)
342        };
343        (half_ulp.clone(), half_ulp, incl_l, incl_r)
344    }
345}
346
347impl Round for mode::HalfEven {
348    type Reverse = Self;
349
350    #[inline]
351    fn round_low_part<F: FnOnce() -> Ordering>(
352        integer: &IBig,
353        low_sign: Sign,
354        low_half_test: F,
355    ) -> Rounding {
356        match low_half_test() {
357            // |rem| < 1/2
358            Ordering::Less => Rounding::NoOp,
359            // |rem| = 1/2
360            Ordering::Equal => {
361                // if integer is odd, +1 if rem > 0, -1 if rem < 0
362                if integer.bit(0) {
363                    match low_sign {
364                        Sign::Positive => Rounding::AddOne,
365                        Sign::Negative => Rounding::SubOne,
366                    }
367                } else {
368                    Rounding::NoOp
369                }
370            }
371            // |rem| > 1/2
372            Ordering::Greater => {
373                // +1 if rem > 0, -1 if rem < 0
374                match low_sign {
375                    Sign::Positive => Rounding::AddOne,
376                    Sign::Negative => Rounding::SubOne,
377                }
378            }
379        }
380    }
381}
382
383impl ErrorBounds for mode::HalfEven {
384    #[inline]
385    fn error_bounds<const B: Word>(
386        f: &FBig<Self, B>,
387    ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
388        if f.precision() == 0 {
389            return (FBig::ZERO, FBig::ZERO, true, true);
390        }
391
392        let mut half_ulp = f.ulp();
393        half_ulp.repr.exponent -= 1;
394        half_ulp.repr.significand = UBig::from_word((B + 1) / 2).into(); // ceil division
395
396        let incl = f.repr.significand.bit(0);
397        (half_ulp.clone(), half_ulp, incl, incl)
398    }
399}
400
401impl Add<Rounding> for IBig {
402    type Output = IBig;
403    #[inline]
404    fn add(self, rhs: Rounding) -> Self::Output {
405        match rhs {
406            Rounding::NoOp => self,
407            Rounding::AddOne => self + IBig::ONE,
408            Rounding::SubOne => self - IBig::ONE,
409        }
410    }
411}
412
413impl Add<Rounding> for &IBig {
414    type Output = IBig;
415    #[inline]
416    fn add(self, rhs: Rounding) -> Self::Output {
417        match rhs {
418            Rounding::NoOp => self.clone(),
419            Rounding::AddOne => self + IBig::ONE,
420            Rounding::SubOne => self - IBig::ONE,
421        }
422    }
423}
424
425impl AddAssign<Rounding> for IBig {
426    #[inline]
427    fn add_assign(&mut self, rhs: Rounding) {
428        match rhs {
429            Rounding::NoOp => {}
430            Rounding::AddOne => *self += IBig::ONE,
431            Rounding::SubOne => *self -= IBig::ONE,
432        }
433    }
434}
435
436#[cfg(test)]
437mod tests {
438    use super::*;
439    use super::{mode::*, Rounding::*};
440
441    #[test]
442    fn test_from_fract() {
443        #[rustfmt::skip]
444        fn test_all_rounding<const B: Word, const D: usize>(
445            input: &(i32, i32, Rounding, Rounding, Rounding, Rounding, Rounding, Rounding),
446        ) {
447            let (value, fract, rnd_zero, rnd_away, rnd_up, rnd_down, rnd_halfeven, rnd_halfaway) = *input;
448            let (value, fract) = (IBig::from(value), IBig::from(fract));
449            assert_eq!(Zero::round_fract::<B>(&value, fract.clone(), D), rnd_zero);
450            assert_eq!(Away::round_fract::<B>(&value, fract.clone(), D), rnd_away);
451            assert_eq!(Up::round_fract::<B>(&value, fract.clone(), D), rnd_up);
452            assert_eq!(Down::round_fract::<B>(&value, fract.clone(), D), rnd_down);
453            assert_eq!(HalfEven::round_fract::<B>(&value, fract.clone(), D), rnd_halfeven);
454            assert_eq!(HalfAway::round_fract::<B>(&value, fract, D), rnd_halfaway);
455        }
456
457        // cases for radix = 2, 2 digit fraction
458        #[rustfmt::skip]
459        let binary_cases = [
460            // (integer value, fraction part, roundings...)
461            // Mode: Zero  , Away  , Up    , Down  , HEven,  HAway
462            ( 0,  3, NoOp  , AddOne, AddOne, NoOp  , AddOne, AddOne),
463            ( 0,  2, NoOp  , AddOne, AddOne, NoOp  , NoOp  , AddOne),
464            ( 0,  1, NoOp  , AddOne, AddOne, NoOp  , NoOp  , NoOp  ),
465            ( 0,  0, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
466            ( 0, -1, NoOp  , SubOne, NoOp  , SubOne, NoOp  , NoOp  ),
467            ( 0, -2, NoOp  , SubOne, NoOp  , SubOne, NoOp  , SubOne),
468            ( 0, -3, NoOp  , SubOne, NoOp  , SubOne, SubOne, SubOne),
469            ( 1,  3, NoOp  , AddOne, AddOne, NoOp  , AddOne, AddOne),
470            ( 1,  2, NoOp  , AddOne, AddOne, NoOp  , AddOne, AddOne),
471            ( 1,  1, NoOp  , AddOne, AddOne, NoOp  , NoOp  , NoOp  ),
472            ( 1,  0, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
473            ( 1, -1, SubOne, NoOp  , NoOp  , SubOne, NoOp  , NoOp  ),
474            ( 1, -2, SubOne, NoOp  , NoOp  , SubOne, SubOne, NoOp  ),
475            ( 1, -3, SubOne, NoOp  , NoOp  , SubOne, SubOne, SubOne),
476            (-1,  3, AddOne, NoOp  , AddOne, NoOp  , AddOne, AddOne),
477            (-1,  2, AddOne, NoOp  , AddOne, NoOp  , AddOne, NoOp  ),
478            (-1,  1, AddOne, NoOp  , AddOne, NoOp  , NoOp  , NoOp  ),
479            (-1,  0, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
480            (-1, -1, NoOp  , SubOne, NoOp  , SubOne, NoOp  , NoOp  ),
481            (-1, -2, NoOp  , SubOne, NoOp  , SubOne, SubOne, SubOne),
482            (-1, -3, NoOp  , SubOne, NoOp  , SubOne, SubOne, SubOne),
483        ];
484        binary_cases.iter().for_each(test_all_rounding::<2, 2>);
485
486        // cases for radix = 3, 1 digit fraction
487        #[rustfmt::skip]
488        let tenary_cases = [
489            // (integer value, fraction part, roundings...)
490            // Mode: Zero,   Away  , Up    , Down  , HEven , HAway
491            ( 0,  2, NoOp,   AddOne, AddOne, NoOp  , AddOne, AddOne),
492            ( 0,  1, NoOp,   AddOne, AddOne, NoOp  , NoOp  , NoOp  ),
493            ( 0,  0, NoOp,   NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
494            ( 0, -1, NoOp,   SubOne, NoOp  , SubOne, NoOp  , NoOp  ),
495            ( 0, -2, NoOp,   SubOne, NoOp  , SubOne, SubOne, SubOne),
496            ( 1,  2, NoOp,   AddOne, AddOne, NoOp  , AddOne, AddOne),
497            ( 1,  1, NoOp,   AddOne, AddOne, NoOp  , NoOp  , NoOp  ),
498            ( 1,  0, NoOp,   NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
499            ( 1, -1, SubOne, NoOp  , NoOp  , SubOne, NoOp  , NoOp  ),
500            ( 1, -2, SubOne, NoOp  , NoOp  , SubOne, SubOne, SubOne),
501            (-1,  2, AddOne, NoOp  , AddOne, NoOp  , AddOne, AddOne),
502            (-1,  1, AddOne, NoOp  , AddOne, NoOp  , NoOp  , NoOp  ),
503            (-1,  0, NoOp,   NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
504            (-1, -1, NoOp,   SubOne, NoOp  , SubOne, NoOp  , NoOp  ),
505            (-1, -2, NoOp,   SubOne, NoOp  , SubOne, SubOne, SubOne),
506        ];
507        tenary_cases.iter().for_each(test_all_rounding::<3, 1>);
508
509        // cases for radix = 10, 1 digit fraction
510        #[rustfmt::skip]
511        let decimal_cases = [
512            // (integer value, fraction part, roundings...)
513            // Mode: Zero  , Away  , Up    , Down  , HEven , HAway
514            ( 0,  7, NoOp  , AddOne, AddOne, NoOp  , AddOne, AddOne),
515            ( 0,  5, NoOp  , AddOne, AddOne, NoOp  , NoOp  , AddOne),
516            ( 0,  2, NoOp  , AddOne, AddOne, NoOp  , NoOp  , NoOp  ),
517            ( 0,  0, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
518            ( 0, -2, NoOp  , SubOne, NoOp  , SubOne, NoOp  , NoOp  ),
519            ( 0, -5, NoOp  , SubOne, NoOp  , SubOne, NoOp  , SubOne),
520            ( 0, -7, NoOp  , SubOne, NoOp  , SubOne, SubOne, SubOne),
521            ( 1,  7, NoOp  , AddOne, AddOne, NoOp  , AddOne, AddOne),
522            ( 1,  5, NoOp  , AddOne, AddOne, NoOp  , AddOne, AddOne),
523            ( 1,  2, NoOp  , AddOne, AddOne, NoOp  , NoOp  , NoOp  ),
524            ( 1,  0, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
525            ( 1, -2, SubOne, NoOp  , NoOp  , SubOne, NoOp  , NoOp  ),
526            ( 1, -5, SubOne, NoOp  , NoOp  , SubOne, SubOne, NoOp  ),
527            ( 1, -7, SubOne, NoOp  , NoOp  , SubOne, SubOne, SubOne),
528            (-1,  7, AddOne, NoOp  , AddOne, NoOp  , AddOne, AddOne),
529            (-1,  5, AddOne, NoOp  , AddOne, NoOp  , AddOne, NoOp  ),
530            (-1,  2, AddOne, NoOp  , AddOne, NoOp  , NoOp  , NoOp  ),
531            (-1,  0, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
532            (-1, -2, NoOp  , SubOne, NoOp  , SubOne, NoOp  , NoOp  ),
533            (-1, -5, NoOp  , SubOne, NoOp  , SubOne, SubOne, SubOne),
534            (-1, -7, NoOp  , SubOne, NoOp  , SubOne, SubOne, SubOne),
535        ];
536        decimal_cases.iter().for_each(test_all_rounding::<10, 1>);
537    }
538
539    #[test]
540    fn test_from_ratio() {
541        #[rustfmt::skip]
542        fn test_all_rounding(
543            input: &(i32, i32, i32, Rounding, Rounding, Rounding, Rounding, Rounding, Rounding),
544        ) {
545            let (value, num, den, rnd_zero, rnd_away, rnd_up, rnd_down, rnd_halfeven, rnd_halfaway) = *input;
546            let (value, num, den) = (IBig::from(value), IBig::from(num), IBig::from(den));
547            assert_eq!(Zero::round_ratio(&value, num.clone(), &den), rnd_zero);
548            assert_eq!(Away::round_ratio(&value, num.clone(), &den), rnd_away);
549            assert_eq!(Up::round_ratio(&value, num.clone(), &den), rnd_up);
550            assert_eq!(Down::round_ratio(&value, num.clone(), &den), rnd_down);
551            assert_eq!(HalfEven::round_ratio(&value, num.clone(), &den), rnd_halfeven);
552            assert_eq!(HalfAway::round_ratio(&value, num, &den), rnd_halfaway);
553        }
554
555        // cases for radix = 2, 2 digit fraction
556        #[rustfmt::skip]
557        let test_cases = [
558            // (integer value, mumerator, denominator, roundings...)
559            // Mode:     Zero  , Away  , Up    , Down  , HEven , HAway
560            ( 0,  0,  2, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
561            ( 0,  1,  2, NoOp  , AddOne, AddOne, NoOp  , NoOp  , AddOne),
562            ( 0, -1,  2, NoOp  , SubOne, NoOp  , SubOne, NoOp  , SubOne),
563            ( 0,  0, -2, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
564            ( 0,  1, -2, NoOp  , SubOne, NoOp  , SubOne, NoOp  , SubOne),
565            ( 0, -1, -2, NoOp  , AddOne, AddOne, NoOp  , NoOp  , AddOne),
566            ( 1,  0,  2, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
567            ( 1,  1,  2, NoOp  , AddOne, AddOne, NoOp  , AddOne, AddOne),
568            ( 1, -1,  2, SubOne, NoOp  , NoOp  , SubOne, SubOne, NoOp  ),
569            ( 1,  0, -2, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
570            ( 1,  1, -2, SubOne, NoOp  , NoOp  , SubOne, SubOne, NoOp  ),
571            ( 1, -1, -2, NoOp  , AddOne, AddOne, NoOp  , AddOne, AddOne),
572            (-1,  0,  2, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
573            (-1,  1,  2, AddOne, NoOp  , AddOne, NoOp  , AddOne, NoOp  ),
574            (-1, -1,  2, NoOp  , SubOne, NoOp  , SubOne, SubOne, SubOne),
575            (-1,  0, -2, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
576            (-1,  1, -2, NoOp  , SubOne, NoOp  , SubOne, SubOne, SubOne),
577            (-1, -1, -2, AddOne, NoOp  , AddOne, NoOp  , AddOne, NoOp  ),
578
579            ( 0, -2,  3, NoOp  , SubOne, NoOp  , SubOne, SubOne, SubOne),
580            ( 0, -1,  3, NoOp  , SubOne, NoOp  , SubOne, NoOp  , NoOp  ),
581            ( 0,  0,  3, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
582            ( 0,  1,  3, NoOp  , AddOne, AddOne, NoOp  , NoOp  , NoOp  ),
583            ( 0,  2,  3, NoOp  , AddOne, AddOne, NoOp  , AddOne, AddOne),
584            ( 0, -2, -3, NoOp  , AddOne, AddOne, NoOp  , AddOne, AddOne),
585            ( 0, -1, -3, NoOp  , AddOne, AddOne, NoOp  , NoOp  , NoOp  ),
586            ( 0,  0, -3, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
587            ( 0,  1, -3, NoOp  , SubOne, NoOp  , SubOne, NoOp  , NoOp  ),
588            ( 0,  2, -3, NoOp  , SubOne, NoOp  , SubOne, SubOne, SubOne),
589            ( 1, -2,  3, SubOne, NoOp  , NoOp  , SubOne, SubOne, SubOne),
590            ( 1, -1,  3, SubOne, NoOp  , NoOp  , SubOne, NoOp  , NoOp  ),
591            ( 1,  0,  3, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
592            ( 1,  1,  3, NoOp  , AddOne, AddOne, NoOp  , NoOp  , NoOp  ),
593            ( 1,  2,  3, NoOp  , AddOne, AddOne, NoOp  , AddOne, AddOne),
594            ( 1, -2, -3, NoOp  , AddOne, AddOne, NoOp  , AddOne, AddOne),
595            ( 1, -1, -3, NoOp  , AddOne, AddOne, NoOp  , NoOp  , NoOp  ),
596            ( 1,  0, -3, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
597            ( 1,  1, -3, SubOne, NoOp  , NoOp  , SubOne, NoOp  , NoOp  ),
598            ( 1,  2, -3, SubOne, NoOp  , NoOp  , SubOne, SubOne, SubOne),
599            (-1, -2,  3, NoOp  , SubOne, NoOp  , SubOne, SubOne, SubOne),
600            (-1, -1,  3, NoOp  , SubOne, NoOp  , SubOne, NoOp  , NoOp  ),
601            (-1,  0,  3, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
602            (-1,  1,  3, AddOne, NoOp  , AddOne, NoOp  , NoOp  , NoOp  ),
603            (-1,  2,  3, AddOne, NoOp  , AddOne, NoOp  , AddOne, AddOne),
604            (-1, -2, -3, AddOne, NoOp  , AddOne, NoOp  , AddOne, AddOne),
605            (-1, -1, -3, AddOne, NoOp  , AddOne, NoOp  , NoOp  , NoOp  ),
606            (-1,  0, -3, NoOp  , NoOp  , NoOp  , NoOp  , NoOp  , NoOp  ),
607            (-1,  1, -3, NoOp  , SubOne, NoOp  , SubOne, NoOp  , NoOp  ),
608            (-1,  2, -3, NoOp  , SubOne, NoOp  , SubOne, SubOne, SubOne),
609        ];
610        test_cases.iter().for_each(test_all_rounding);
611    }
612}