1use core::cmp::Ordering;
4use core::ops::{Add, AddAssign};
5use dashu_base::{AbsOrd, Approximation, BitTest, EstimatedLog2, Sign, Signed};
6use dashu_int::{IBig, UBig, Word};
7
8use crate::FBig;
9
10pub mod mode {
35 #[derive(Clone, Copy)]
37 pub struct Zero;
38
39 #[derive(Clone, Copy)]
41 pub struct Away;
42
43 #[derive(Clone, Copy)]
45 pub struct Up;
46
47 #[derive(Clone, Copy)]
49 pub struct Down;
50
51 #[derive(Clone, Copy)]
53 pub struct HalfEven;
54
55 #[derive(Clone, Copy)]
57 pub struct HalfAway;
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum Rounding {
69 NoOp,
71
72 AddOne,
74
75 SubOne,
77}
78
79pub type Rounded<T> = Approximation<T, Rounding>;
84
85pub trait Round: Copy {
87 type Reverse: Round;
89
90 const IS_ROUND_TOWARD_NEGATIVE: bool;
94
95 fn round_low_part<F: FnOnce() -> Ordering>(
103 integer: &IBig,
104 low_sign: Sign,
105 low_half_test: F,
106 ) -> Rounding;
107
108 #[inline]
111 fn round_fract<const B: Word>(integer: &IBig, fract: IBig, precision: usize) -> Rounding {
112 debug_assert!({
119 let (lb, _ub) = fract.log2_bounds(); let (_b_lb, b_ub) = B.log2_bounds();
121 lb < b_ub * precision as f32
123 });
124
125 if fract.is_zero() {
126 return Rounding::NoOp;
127 }
128 let (fsign, fmag) = fract.into_parts();
129
130 let test = || {
131 let (lb, ub) = fmag.log2_bounds();
133 let (b_lb, b_ub) = B.log2_bounds();
134
135 if lb + 0.999 > b_ub * precision as f32 {
137 Ordering::Greater
138 } else if ub + 1.001 < b_lb * precision as f32 {
139 Ordering::Less
140 } else {
141 (fmag << 1).cmp(&UBig::from_word(B).pow(precision))
142 }
143 };
144 Self::round_low_part::<_>(integer, fsign, test)
145 }
146
147 #[inline]
150 fn round_ratio(integer: &IBig, num: IBig, den: &IBig) -> Rounding {
151 assert!(!den.is_zero() && num.abs_cmp(den).is_le());
152
153 if num.is_zero() {
154 return Rounding::NoOp;
155 }
156 let (nsign, nmag) = num.into_parts();
157 Self::round_low_part::<_>(integer, nsign * den.sign(), || {
158 if den.is_positive() {
159 IBig::from(nmag << 1).cmp(den)
160 } else {
161 den.cmp(&-(nmag << 1))
162 }
163 })
164 }
165}
166
167pub trait ErrorBounds: Round {
169 fn error_bounds<const B: Word>(f: &FBig<Self, B>)
175 -> (FBig<Self, B>, FBig<Self, B>, bool, bool);
176}
177
178impl Round for mode::Zero {
179 type Reverse = mode::Away;
180 const IS_ROUND_TOWARD_NEGATIVE: bool = false;
181
182 #[inline]
183 fn round_low_part<F: FnOnce() -> Ordering>(
184 integer: &IBig,
185 low_sign: Sign,
186 _low_half_test: F,
187 ) -> Rounding {
188 if integer.is_zero() {
189 return Rounding::NoOp;
190 }
191 match (integer.sign(), low_sign) {
192 (Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => Rounding::NoOp,
193 (Sign::Positive, Sign::Negative) => Rounding::SubOne,
194 (Sign::Negative, Sign::Positive) => Rounding::AddOne,
195 }
196 }
197}
198
199impl ErrorBounds for mode::Zero {
200 #[inline]
201 fn error_bounds<const B: Word>(
202 f: &FBig<Self, B>,
203 ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
204 if f.precision() == 0 {
205 (FBig::ZERO, FBig::ZERO, true, true)
206 } else if f.repr().is_pos_zero() {
207 (f.ulp(), f.ulp(), false, false)
214 } else {
215 match f.repr().sign() {
216 Sign::Positive => (FBig::ZERO, f.ulp(), true, false),
217 Sign::Negative => (f.ulp(), FBig::ZERO, false, true),
218 }
219 }
220 }
221}
222
223impl Round for mode::Away {
224 type Reverse = mode::Zero;
225 const IS_ROUND_TOWARD_NEGATIVE: bool = false;
226
227 #[inline]
228 fn round_low_part<F: FnOnce() -> Ordering>(
229 integer: &IBig,
230 low_sign: Sign,
231 _low_half_test: F,
232 ) -> Rounding {
233 if integer.is_zero() {
234 match low_sign {
235 Sign::Positive => Rounding::AddOne,
236 Sign::Negative => Rounding::SubOne,
237 }
238 } else {
239 match (integer.sign(), low_sign) {
240 (Sign::Positive, Sign::Positive) => Rounding::AddOne,
241 (Sign::Negative, Sign::Negative) => Rounding::SubOne,
242 (Sign::Positive, Sign::Negative) | (Sign::Negative, Sign::Positive) => {
243 Rounding::NoOp
244 }
245 }
246 }
247 }
248}
249
250impl ErrorBounds for mode::Away {
251 #[inline]
252 fn error_bounds<const B: Word>(
253 f: &FBig<Self, B>,
254 ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
255 if f.precision() == 0 {
256 (FBig::ZERO, FBig::ZERO, true, true)
260 } else {
261 match f.repr().sign() {
262 Sign::Positive => (f.ulp(), FBig::ZERO, false, true),
263 Sign::Negative => (FBig::ZERO, f.ulp(), true, false),
264 }
265 }
266 }
267}
268
269impl Round for mode::Down {
270 type Reverse = mode::Up;
271 const IS_ROUND_TOWARD_NEGATIVE: bool = true;
272
273 #[inline]
274 fn round_low_part<F: FnOnce() -> Ordering>(
275 _integer: &IBig,
276 low_sign: Sign,
277 _low_half_test: F,
278 ) -> Rounding {
279 if low_sign == Sign::Negative {
281 Rounding::SubOne
282 } else {
283 Rounding::NoOp
284 }
285 }
286}
287
288impl ErrorBounds for mode::Down {
289 #[inline]
290 fn error_bounds<const B: Word>(
291 f: &FBig<Self, B>,
292 ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
293 (FBig::ZERO, f.ulp(), true, false)
294 }
295}
296
297impl Round for mode::Up {
298 type Reverse = mode::Down;
299 const IS_ROUND_TOWARD_NEGATIVE: bool = false;
300
301 #[inline]
302 fn round_low_part<F: FnOnce() -> Ordering>(
303 _integer: &IBig,
304 low_sign: Sign,
305 _low_half_test: F,
306 ) -> Rounding {
307 if low_sign == Sign::Positive {
309 Rounding::AddOne
310 } else {
311 Rounding::NoOp
312 }
313 }
314}
315
316impl ErrorBounds for mode::Up {
317 #[inline]
318 fn error_bounds<const B: Word>(
319 f: &FBig<Self, B>,
320 ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
321 (f.ulp(), FBig::ZERO, false, true)
322 }
323}
324
325impl Round for mode::HalfAway {
326 type Reverse = Self;
327 const IS_ROUND_TOWARD_NEGATIVE: bool = false;
328
329 #[inline]
330 fn round_low_part<F: FnOnce() -> Ordering>(
331 integer: &IBig,
332 low_sign: Sign,
333 low_half_test: F,
334 ) -> Rounding {
335 match low_half_test() {
336 Ordering::Less => Rounding::NoOp,
338 Ordering::Equal => {
340 if integer >= &IBig::ZERO && low_sign == Sign::Positive {
342 Rounding::AddOne
343 } else if integer <= &IBig::ZERO && low_sign == Sign::Negative {
344 Rounding::SubOne
345 } else {
346 Rounding::NoOp
347 }
348 }
349 Ordering::Greater => {
351 match low_sign {
353 Sign::Positive => Rounding::AddOne,
354 Sign::Negative => Rounding::SubOne,
355 }
356 }
357 }
358 }
359}
360
361impl ErrorBounds for mode::HalfAway {
362 #[inline]
363 fn error_bounds<const B: Word>(
364 f: &FBig<Self, B>,
365 ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
366 if f.precision() == 0 {
367 return (FBig::ZERO, FBig::ZERO, true, true);
368 }
369
370 let mut half_ulp = f.ulp();
371 half_ulp.repr.exponent -= 1;
372 half_ulp.repr.significand = UBig::from_word((B + 1) / 2).into(); let (incl_l, incl_r) = if f.repr.is_pos_zero() {
375 (false, false)
380 } else if f.repr.sign() == Sign::Negative {
381 (false, true)
382 } else {
383 (true, false)
384 };
385 (half_ulp.clone(), half_ulp, incl_l, incl_r)
386 }
387}
388
389impl Round for mode::HalfEven {
390 type Reverse = Self;
391 const IS_ROUND_TOWARD_NEGATIVE: bool = false;
392
393 #[inline]
394 fn round_low_part<F: FnOnce() -> Ordering>(
395 integer: &IBig,
396 low_sign: Sign,
397 low_half_test: F,
398 ) -> Rounding {
399 match low_half_test() {
400 Ordering::Less => Rounding::NoOp,
402 Ordering::Equal => {
404 if integer.bit(0) {
406 match low_sign {
407 Sign::Positive => Rounding::AddOne,
408 Sign::Negative => Rounding::SubOne,
409 }
410 } else {
411 Rounding::NoOp
412 }
413 }
414 Ordering::Greater => {
416 match low_sign {
418 Sign::Positive => Rounding::AddOne,
419 Sign::Negative => Rounding::SubOne,
420 }
421 }
422 }
423 }
424}
425
426impl ErrorBounds for mode::HalfEven {
427 #[inline]
428 fn error_bounds<const B: Word>(
429 f: &FBig<Self, B>,
430 ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
431 if f.precision() == 0 {
432 return (FBig::ZERO, FBig::ZERO, true, true);
433 }
434
435 let mut half_ulp = f.ulp();
436 half_ulp.repr.exponent -= 1;
437 half_ulp.repr.significand = UBig::from_word((B + 1) / 2).into(); let (incl_l, incl_r) = if f.repr.is_neg_zero() {
443 (false, true)
444 } else {
445 let incl = f.repr.significand.bit(0);
446 (incl, incl)
447 };
448 (half_ulp.clone(), half_ulp, incl_l, incl_r)
449 }
450}
451
452impl Add<Rounding> for IBig {
453 type Output = IBig;
454 #[inline]
455 fn add(self, rhs: Rounding) -> Self::Output {
456 match rhs {
457 Rounding::NoOp => self,
458 Rounding::AddOne => self + IBig::ONE,
459 Rounding::SubOne => self - IBig::ONE,
460 }
461 }
462}
463
464impl Add<Rounding> for &IBig {
465 type Output = IBig;
466 #[inline]
467 fn add(self, rhs: Rounding) -> Self::Output {
468 match rhs {
469 Rounding::NoOp => self.clone(),
470 Rounding::AddOne => self + IBig::ONE,
471 Rounding::SubOne => self - IBig::ONE,
472 }
473 }
474}
475
476impl AddAssign<Rounding> for IBig {
477 #[inline]
478 fn add_assign(&mut self, rhs: Rounding) {
479 match rhs {
480 Rounding::NoOp => {}
481 Rounding::AddOne => *self += IBig::ONE,
482 Rounding::SubOne => *self -= IBig::ONE,
483 }
484 }
485}
486
487#[cfg(test)]
488mod tests {
489 use super::*;
490 use super::{mode::*, Rounding::*};
491
492 #[test]
493 fn test_from_fract() {
494 #[rustfmt::skip]
495 fn test_all_rounding<const B: Word, const D: usize>(
496 input: &(i32, i32, Rounding, Rounding, Rounding, Rounding, Rounding, Rounding),
497 ) {
498 let (value, fract, rnd_zero, rnd_away, rnd_up, rnd_down, rnd_halfeven, rnd_halfaway) = *input;
499 let (value, fract) = (IBig::from(value), IBig::from(fract));
500 assert_eq!(Zero::round_fract::<B>(&value, fract.clone(), D), rnd_zero);
501 assert_eq!(Away::round_fract::<B>(&value, fract.clone(), D), rnd_away);
502 assert_eq!(Up::round_fract::<B>(&value, fract.clone(), D), rnd_up);
503 assert_eq!(Down::round_fract::<B>(&value, fract.clone(), D), rnd_down);
504 assert_eq!(HalfEven::round_fract::<B>(&value, fract.clone(), D), rnd_halfeven);
505 assert_eq!(HalfAway::round_fract::<B>(&value, fract, D), rnd_halfaway);
506 }
507
508 #[rustfmt::skip]
510 let binary_cases = [
511 ( 0, 3, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
514 ( 0, 2, NoOp , AddOne, AddOne, NoOp , NoOp , AddOne),
515 ( 0, 1, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
516 ( 0, 0, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
517 ( 0, -1, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
518 ( 0, -2, NoOp , SubOne, NoOp , SubOne, NoOp , SubOne),
519 ( 0, -3, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
520 ( 1, 3, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
521 ( 1, 2, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
522 ( 1, 1, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
523 ( 1, 0, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
524 ( 1, -1, SubOne, NoOp , NoOp , SubOne, NoOp , NoOp ),
525 ( 1, -2, SubOne, NoOp , NoOp , SubOne, SubOne, NoOp ),
526 ( 1, -3, SubOne, NoOp , NoOp , SubOne, SubOne, SubOne),
527 (-1, 3, AddOne, NoOp , AddOne, NoOp , AddOne, AddOne),
528 (-1, 2, AddOne, NoOp , AddOne, NoOp , AddOne, NoOp ),
529 (-1, 1, AddOne, NoOp , AddOne, NoOp , NoOp , NoOp ),
530 (-1, 0, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
531 (-1, -1, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
532 (-1, -2, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
533 (-1, -3, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
534 ];
535 binary_cases.iter().for_each(test_all_rounding::<2, 2>);
536
537 #[rustfmt::skip]
539 let tenary_cases = [
540 ( 0, 2, NoOp, AddOne, AddOne, NoOp , AddOne, AddOne),
543 ( 0, 1, NoOp, AddOne, AddOne, NoOp , NoOp , NoOp ),
544 ( 0, 0, NoOp, NoOp , NoOp , NoOp , NoOp , NoOp ),
545 ( 0, -1, NoOp, SubOne, NoOp , SubOne, NoOp , NoOp ),
546 ( 0, -2, NoOp, SubOne, NoOp , SubOne, SubOne, SubOne),
547 ( 1, 2, NoOp, AddOne, AddOne, NoOp , AddOne, AddOne),
548 ( 1, 1, NoOp, AddOne, AddOne, NoOp , NoOp , NoOp ),
549 ( 1, 0, NoOp, NoOp , NoOp , NoOp , NoOp , NoOp ),
550 ( 1, -1, SubOne, NoOp , NoOp , SubOne, NoOp , NoOp ),
551 ( 1, -2, SubOne, NoOp , NoOp , SubOne, SubOne, SubOne),
552 (-1, 2, AddOne, NoOp , AddOne, NoOp , AddOne, AddOne),
553 (-1, 1, AddOne, NoOp , AddOne, NoOp , NoOp , NoOp ),
554 (-1, 0, NoOp, NoOp , NoOp , NoOp , NoOp , NoOp ),
555 (-1, -1, NoOp, SubOne, NoOp , SubOne, NoOp , NoOp ),
556 (-1, -2, NoOp, SubOne, NoOp , SubOne, SubOne, SubOne),
557 ];
558 tenary_cases.iter().for_each(test_all_rounding::<3, 1>);
559
560 #[rustfmt::skip]
562 let decimal_cases = [
563 ( 0, 7, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
566 ( 0, 5, NoOp , AddOne, AddOne, NoOp , NoOp , AddOne),
567 ( 0, 2, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
568 ( 0, 0, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
569 ( 0, -2, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
570 ( 0, -5, NoOp , SubOne, NoOp , SubOne, NoOp , SubOne),
571 ( 0, -7, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
572 ( 1, 7, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
573 ( 1, 5, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
574 ( 1, 2, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
575 ( 1, 0, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
576 ( 1, -2, SubOne, NoOp , NoOp , SubOne, NoOp , NoOp ),
577 ( 1, -5, SubOne, NoOp , NoOp , SubOne, SubOne, NoOp ),
578 ( 1, -7, SubOne, NoOp , NoOp , SubOne, SubOne, SubOne),
579 (-1, 7, AddOne, NoOp , AddOne, NoOp , AddOne, AddOne),
580 (-1, 5, AddOne, NoOp , AddOne, NoOp , AddOne, NoOp ),
581 (-1, 2, AddOne, NoOp , AddOne, NoOp , NoOp , NoOp ),
582 (-1, 0, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
583 (-1, -2, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
584 (-1, -5, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
585 (-1, -7, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
586 ];
587 decimal_cases.iter().for_each(test_all_rounding::<10, 1>);
588 }
589
590 #[test]
591 fn test_from_ratio() {
592 #[rustfmt::skip]
593 fn test_all_rounding(
594 input: &(i32, i32, i32, Rounding, Rounding, Rounding, Rounding, Rounding, Rounding),
595 ) {
596 let (value, num, den, rnd_zero, rnd_away, rnd_up, rnd_down, rnd_halfeven, rnd_halfaway) = *input;
597 let (value, num, den) = (IBig::from(value), IBig::from(num), IBig::from(den));
598 assert_eq!(Zero::round_ratio(&value, num.clone(), &den), rnd_zero);
599 assert_eq!(Away::round_ratio(&value, num.clone(), &den), rnd_away);
600 assert_eq!(Up::round_ratio(&value, num.clone(), &den), rnd_up);
601 assert_eq!(Down::round_ratio(&value, num.clone(), &den), rnd_down);
602 assert_eq!(HalfEven::round_ratio(&value, num.clone(), &den), rnd_halfeven);
603 assert_eq!(HalfAway::round_ratio(&value, num, &den), rnd_halfaway);
604 }
605
606 #[rustfmt::skip]
608 let test_cases = [
609 ( 0, 0, 2, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
612 ( 0, 1, 2, NoOp , AddOne, AddOne, NoOp , NoOp , AddOne),
613 ( 0, -1, 2, NoOp , SubOne, NoOp , SubOne, NoOp , SubOne),
614 ( 0, 0, -2, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
615 ( 0, 1, -2, NoOp , SubOne, NoOp , SubOne, NoOp , SubOne),
616 ( 0, -1, -2, NoOp , AddOne, AddOne, NoOp , NoOp , AddOne),
617 ( 1, 0, 2, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
618 ( 1, 1, 2, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
619 ( 1, -1, 2, SubOne, NoOp , NoOp , SubOne, SubOne, NoOp ),
620 ( 1, 0, -2, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
621 ( 1, 1, -2, SubOne, NoOp , NoOp , SubOne, SubOne, NoOp ),
622 ( 1, -1, -2, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
623 (-1, 0, 2, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
624 (-1, 1, 2, AddOne, NoOp , AddOne, NoOp , AddOne, NoOp ),
625 (-1, -1, 2, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
626 (-1, 0, -2, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
627 (-1, 1, -2, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
628 (-1, -1, -2, AddOne, NoOp , AddOne, NoOp , AddOne, NoOp ),
629
630 ( 0, -2, 3, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
631 ( 0, -1, 3, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
632 ( 0, 0, 3, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
633 ( 0, 1, 3, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
634 ( 0, 2, 3, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
635 ( 0, -2, -3, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
636 ( 0, -1, -3, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
637 ( 0, 0, -3, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
638 ( 0, 1, -3, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
639 ( 0, 2, -3, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
640 ( 1, -2, 3, SubOne, NoOp , NoOp , SubOne, SubOne, SubOne),
641 ( 1, -1, 3, SubOne, NoOp , NoOp , SubOne, NoOp , NoOp ),
642 ( 1, 0, 3, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
643 ( 1, 1, 3, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
644 ( 1, 2, 3, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
645 ( 1, -2, -3, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
646 ( 1, -1, -3, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
647 ( 1, 0, -3, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
648 ( 1, 1, -3, SubOne, NoOp , NoOp , SubOne, NoOp , NoOp ),
649 ( 1, 2, -3, SubOne, NoOp , NoOp , SubOne, SubOne, SubOne),
650 (-1, -2, 3, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
651 (-1, -1, 3, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
652 (-1, 0, 3, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
653 (-1, 1, 3, AddOne, NoOp , AddOne, NoOp , NoOp , NoOp ),
654 (-1, 2, 3, AddOne, NoOp , AddOne, NoOp , AddOne, AddOne),
655 (-1, -2, -3, AddOne, NoOp , AddOne, NoOp , AddOne, AddOne),
656 (-1, -1, -3, AddOne, NoOp , AddOne, NoOp , NoOp , NoOp ),
657 (-1, 0, -3, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
658 (-1, 1, -3, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
659 (-1, 2, -3, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
660 ];
661 test_cases.iter().for_each(test_all_rounding);
662 }
663}