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>(
98 integer: &IBig,
99 low_sign: Sign,
100 low_half_test: F,
101 ) -> Rounding;
102
103 #[inline]
106 fn round_fract<const B: Word>(integer: &IBig, fract: IBig, precision: usize) -> Rounding {
107 debug_assert!({
114 let (lb, _ub) = fract.log2_bounds(); let (_b_lb, b_ub) = B.log2_bounds();
116 lb < b_ub * precision as f32
118 });
119
120 if fract.is_zero() {
121 return Rounding::NoOp;
122 }
123 let (fsign, fmag) = fract.into_parts();
124
125 let test = || {
126 let (lb, ub) = fmag.log2_bounds();
128 let (b_lb, b_ub) = B.log2_bounds();
129
130 if lb + 0.999 > b_ub * precision as f32 {
132 Ordering::Greater
133 } else if ub + 1.001 < b_lb * precision as f32 {
134 Ordering::Less
135 } else {
136 (fmag << 1).cmp(&UBig::from_word(B).pow(precision))
137 }
138 };
139 Self::round_low_part::<_>(integer, fsign, test)
140 }
141
142 #[inline]
145 fn round_ratio(integer: &IBig, num: IBig, den: &IBig) -> Rounding {
146 assert!(!den.is_zero() && num.abs_cmp(den).is_le());
147
148 if num.is_zero() {
149 return Rounding::NoOp;
150 }
151 let (nsign, nmag) = num.into_parts();
152 Self::round_low_part::<_>(integer, nsign * den.sign(), || {
153 if den.is_positive() {
154 IBig::from(nmag << 1).cmp(den)
155 } else {
156 den.cmp(&-(nmag << 1))
157 }
158 })
159 }
160}
161
162pub trait ErrorBounds: Round {
164 fn error_bounds<const B: Word>(f: &FBig<Self, B>)
170 -> (FBig<Self, B>, FBig<Self, B>, bool, bool);
171}
172
173impl Round for mode::Zero {
174 type Reverse = mode::Away;
175 const IS_ROUND_TOWARD_NEGATIVE: bool = false;
176
177 #[inline]
178 fn round_low_part<F: FnOnce() -> Ordering>(
179 integer: &IBig,
180 low_sign: Sign,
181 _low_half_test: F,
182 ) -> Rounding {
183 if integer.is_zero() {
184 return Rounding::NoOp;
185 }
186 match (integer.sign(), low_sign) {
187 (Sign::Positive, Sign::Positive) | (Sign::Negative, Sign::Negative) => Rounding::NoOp,
188 (Sign::Positive, Sign::Negative) => Rounding::SubOne,
189 (Sign::Negative, Sign::Positive) => Rounding::AddOne,
190 }
191 }
192}
193
194impl ErrorBounds for mode::Zero {
195 #[inline]
196 fn error_bounds<const B: Word>(
197 f: &FBig<Self, B>,
198 ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
199 if f.precision() == 0 {
200 (FBig::ZERO, FBig::ZERO, true, true)
201 } else if f.repr().is_pos_zero() {
202 (f.ulp(), f.ulp(), false, false)
209 } else {
210 match f.repr().sign() {
211 Sign::Positive => (FBig::ZERO, f.ulp(), true, false),
212 Sign::Negative => (f.ulp(), FBig::ZERO, false, true),
213 }
214 }
215 }
216}
217
218impl Round for mode::Away {
219 type Reverse = mode::Zero;
220 const IS_ROUND_TOWARD_NEGATIVE: bool = false;
221
222 #[inline]
223 fn round_low_part<F: FnOnce() -> Ordering>(
224 integer: &IBig,
225 low_sign: Sign,
226 _low_half_test: F,
227 ) -> Rounding {
228 if integer.is_zero() {
229 match low_sign {
230 Sign::Positive => Rounding::AddOne,
231 Sign::Negative => Rounding::SubOne,
232 }
233 } else {
234 match (integer.sign(), low_sign) {
235 (Sign::Positive, Sign::Positive) => Rounding::AddOne,
236 (Sign::Negative, Sign::Negative) => Rounding::SubOne,
237 (Sign::Positive, Sign::Negative) | (Sign::Negative, Sign::Positive) => {
238 Rounding::NoOp
239 }
240 }
241 }
242 }
243}
244
245impl ErrorBounds for mode::Away {
246 #[inline]
247 fn error_bounds<const B: Word>(
248 f: &FBig<Self, B>,
249 ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
250 if f.precision() == 0 {
251 (FBig::ZERO, FBig::ZERO, true, true)
255 } else {
256 match f.repr().sign() {
257 Sign::Positive => (f.ulp(), FBig::ZERO, false, true),
258 Sign::Negative => (FBig::ZERO, f.ulp(), true, false),
259 }
260 }
261 }
262}
263
264impl Round for mode::Down {
265 type Reverse = mode::Up;
266 const IS_ROUND_TOWARD_NEGATIVE: bool = true;
267
268 #[inline]
269 fn round_low_part<F: FnOnce() -> Ordering>(
270 _integer: &IBig,
271 low_sign: Sign,
272 _low_half_test: F,
273 ) -> Rounding {
274 if low_sign == Sign::Negative {
276 Rounding::SubOne
277 } else {
278 Rounding::NoOp
279 }
280 }
281}
282
283impl ErrorBounds for mode::Down {
284 #[inline]
285 fn error_bounds<const B: Word>(
286 f: &FBig<Self, B>,
287 ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
288 (FBig::ZERO, f.ulp(), true, false)
289 }
290}
291
292impl Round for mode::Up {
293 type Reverse = mode::Down;
294 const IS_ROUND_TOWARD_NEGATIVE: bool = false;
295
296 #[inline]
297 fn round_low_part<F: FnOnce() -> Ordering>(
298 _integer: &IBig,
299 low_sign: Sign,
300 _low_half_test: F,
301 ) -> Rounding {
302 if low_sign == Sign::Positive {
304 Rounding::AddOne
305 } else {
306 Rounding::NoOp
307 }
308 }
309}
310
311impl ErrorBounds for mode::Up {
312 #[inline]
313 fn error_bounds<const B: Word>(
314 f: &FBig<Self, B>,
315 ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
316 (f.ulp(), FBig::ZERO, false, true)
317 }
318}
319
320impl Round for mode::HalfAway {
321 type Reverse = Self;
322 const IS_ROUND_TOWARD_NEGATIVE: bool = false;
323
324 #[inline]
325 fn round_low_part<F: FnOnce() -> Ordering>(
326 integer: &IBig,
327 low_sign: Sign,
328 low_half_test: F,
329 ) -> Rounding {
330 match low_half_test() {
331 Ordering::Less => Rounding::NoOp,
333 Ordering::Equal => {
335 if integer >= &IBig::ZERO && low_sign == Sign::Positive {
337 Rounding::AddOne
338 } else if integer <= &IBig::ZERO && low_sign == Sign::Negative {
339 Rounding::SubOne
340 } else {
341 Rounding::NoOp
342 }
343 }
344 Ordering::Greater => {
346 match low_sign {
348 Sign::Positive => Rounding::AddOne,
349 Sign::Negative => Rounding::SubOne,
350 }
351 }
352 }
353 }
354}
355
356impl ErrorBounds for mode::HalfAway {
357 #[inline]
358 fn error_bounds<const B: Word>(
359 f: &FBig<Self, B>,
360 ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
361 if f.precision() == 0 {
362 return (FBig::ZERO, FBig::ZERO, true, true);
363 }
364
365 let mut half_ulp = f.ulp();
366 half_ulp.repr.exponent -= 1;
367 half_ulp.repr.significand = UBig::from_word((B + 1) / 2).into(); let (incl_l, incl_r) = if f.repr.is_pos_zero() {
370 (false, false)
375 } else if f.repr.sign() == Sign::Negative {
376 (false, true)
377 } else {
378 (true, false)
379 };
380 (half_ulp.clone(), half_ulp, incl_l, incl_r)
381 }
382}
383
384impl Round for mode::HalfEven {
385 type Reverse = Self;
386 const IS_ROUND_TOWARD_NEGATIVE: bool = false;
387
388 #[inline]
389 fn round_low_part<F: FnOnce() -> Ordering>(
390 integer: &IBig,
391 low_sign: Sign,
392 low_half_test: F,
393 ) -> Rounding {
394 match low_half_test() {
395 Ordering::Less => Rounding::NoOp,
397 Ordering::Equal => {
399 if integer.bit(0) {
401 match low_sign {
402 Sign::Positive => Rounding::AddOne,
403 Sign::Negative => Rounding::SubOne,
404 }
405 } else {
406 Rounding::NoOp
407 }
408 }
409 Ordering::Greater => {
411 match low_sign {
413 Sign::Positive => Rounding::AddOne,
414 Sign::Negative => Rounding::SubOne,
415 }
416 }
417 }
418 }
419}
420
421impl ErrorBounds for mode::HalfEven {
422 #[inline]
423 fn error_bounds<const B: Word>(
424 f: &FBig<Self, B>,
425 ) -> (FBig<Self, B>, FBig<Self, B>, bool, bool) {
426 if f.precision() == 0 {
427 return (FBig::ZERO, FBig::ZERO, true, true);
428 }
429
430 let mut half_ulp = f.ulp();
431 half_ulp.repr.exponent -= 1;
432 half_ulp.repr.significand = UBig::from_word((B + 1) / 2).into(); let (incl_l, incl_r) = if f.repr.is_neg_zero() {
438 (false, true)
439 } else {
440 let incl = f.repr.significand.bit(0);
441 (incl, incl)
442 };
443 (half_ulp.clone(), half_ulp, incl_l, incl_r)
444 }
445}
446
447impl Add<Rounding> for IBig {
448 type Output = IBig;
449 #[inline]
450 fn add(self, rhs: Rounding) -> Self::Output {
451 match rhs {
452 Rounding::NoOp => self,
453 Rounding::AddOne => self + IBig::ONE,
454 Rounding::SubOne => self - IBig::ONE,
455 }
456 }
457}
458
459impl Add<Rounding> for &IBig {
460 type Output = IBig;
461 #[inline]
462 fn add(self, rhs: Rounding) -> Self::Output {
463 match rhs {
464 Rounding::NoOp => self.clone(),
465 Rounding::AddOne => self + IBig::ONE,
466 Rounding::SubOne => self - IBig::ONE,
467 }
468 }
469}
470
471impl AddAssign<Rounding> for IBig {
472 #[inline]
473 fn add_assign(&mut self, rhs: Rounding) {
474 match rhs {
475 Rounding::NoOp => {}
476 Rounding::AddOne => *self += IBig::ONE,
477 Rounding::SubOne => *self -= IBig::ONE,
478 }
479 }
480}
481
482#[cfg(test)]
483mod tests {
484 use super::*;
485 use super::{mode::*, Rounding::*};
486
487 #[test]
488 fn test_from_fract() {
489 #[rustfmt::skip]
490 fn test_all_rounding<const B: Word, const D: usize>(
491 input: &(i32, i32, Rounding, Rounding, Rounding, Rounding, Rounding, Rounding),
492 ) {
493 let (value, fract, rnd_zero, rnd_away, rnd_up, rnd_down, rnd_halfeven, rnd_halfaway) = *input;
494 let (value, fract) = (IBig::from(value), IBig::from(fract));
495 assert_eq!(Zero::round_fract::<B>(&value, fract.clone(), D), rnd_zero);
496 assert_eq!(Away::round_fract::<B>(&value, fract.clone(), D), rnd_away);
497 assert_eq!(Up::round_fract::<B>(&value, fract.clone(), D), rnd_up);
498 assert_eq!(Down::round_fract::<B>(&value, fract.clone(), D), rnd_down);
499 assert_eq!(HalfEven::round_fract::<B>(&value, fract.clone(), D), rnd_halfeven);
500 assert_eq!(HalfAway::round_fract::<B>(&value, fract, D), rnd_halfaway);
501 }
502
503 #[rustfmt::skip]
505 let binary_cases = [
506 ( 0, 3, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
509 ( 0, 2, NoOp , AddOne, AddOne, NoOp , NoOp , AddOne),
510 ( 0, 1, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
511 ( 0, 0, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
512 ( 0, -1, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
513 ( 0, -2, NoOp , SubOne, NoOp , SubOne, NoOp , SubOne),
514 ( 0, -3, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
515 ( 1, 3, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
516 ( 1, 2, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
517 ( 1, 1, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
518 ( 1, 0, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
519 ( 1, -1, SubOne, NoOp , NoOp , SubOne, NoOp , NoOp ),
520 ( 1, -2, SubOne, NoOp , NoOp , SubOne, SubOne, NoOp ),
521 ( 1, -3, SubOne, NoOp , NoOp , SubOne, SubOne, SubOne),
522 (-1, 3, AddOne, NoOp , AddOne, NoOp , AddOne, AddOne),
523 (-1, 2, AddOne, NoOp , AddOne, NoOp , AddOne, NoOp ),
524 (-1, 1, AddOne, NoOp , AddOne, NoOp , NoOp , NoOp ),
525 (-1, 0, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
526 (-1, -1, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
527 (-1, -2, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
528 (-1, -3, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
529 ];
530 binary_cases.iter().for_each(test_all_rounding::<2, 2>);
531
532 #[rustfmt::skip]
534 let tenary_cases = [
535 ( 0, 2, NoOp, AddOne, AddOne, NoOp , AddOne, AddOne),
538 ( 0, 1, NoOp, AddOne, AddOne, NoOp , NoOp , NoOp ),
539 ( 0, 0, NoOp, NoOp , NoOp , NoOp , NoOp , NoOp ),
540 ( 0, -1, NoOp, SubOne, NoOp , SubOne, NoOp , NoOp ),
541 ( 0, -2, NoOp, SubOne, NoOp , SubOne, SubOne, SubOne),
542 ( 1, 2, NoOp, AddOne, AddOne, NoOp , AddOne, AddOne),
543 ( 1, 1, NoOp, AddOne, AddOne, NoOp , NoOp , NoOp ),
544 ( 1, 0, NoOp, NoOp , NoOp , NoOp , NoOp , NoOp ),
545 ( 1, -1, SubOne, NoOp , NoOp , SubOne, NoOp , NoOp ),
546 ( 1, -2, SubOne, NoOp , NoOp , SubOne, SubOne, SubOne),
547 (-1, 2, AddOne, NoOp , AddOne, NoOp , AddOne, AddOne),
548 (-1, 1, AddOne, NoOp , AddOne, NoOp , NoOp , NoOp ),
549 (-1, 0, NoOp, NoOp , NoOp , NoOp , NoOp , NoOp ),
550 (-1, -1, NoOp, SubOne, NoOp , SubOne, NoOp , NoOp ),
551 (-1, -2, NoOp, SubOne, NoOp , SubOne, SubOne, SubOne),
552 ];
553 tenary_cases.iter().for_each(test_all_rounding::<3, 1>);
554
555 #[rustfmt::skip]
557 let decimal_cases = [
558 ( 0, 7, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
561 ( 0, 5, NoOp , AddOne, AddOne, NoOp , NoOp , AddOne),
562 ( 0, 2, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
563 ( 0, 0, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
564 ( 0, -2, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
565 ( 0, -5, NoOp , SubOne, NoOp , SubOne, NoOp , SubOne),
566 ( 0, -7, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
567 ( 1, 7, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
568 ( 1, 5, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
569 ( 1, 2, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
570 ( 1, 0, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
571 ( 1, -2, SubOne, NoOp , NoOp , SubOne, NoOp , NoOp ),
572 ( 1, -5, SubOne, NoOp , NoOp , SubOne, SubOne, NoOp ),
573 ( 1, -7, SubOne, NoOp , NoOp , SubOne, SubOne, SubOne),
574 (-1, 7, AddOne, NoOp , AddOne, NoOp , AddOne, AddOne),
575 (-1, 5, AddOne, NoOp , AddOne, NoOp , AddOne, NoOp ),
576 (-1, 2, AddOne, NoOp , AddOne, NoOp , NoOp , NoOp ),
577 (-1, 0, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
578 (-1, -2, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
579 (-1, -5, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
580 (-1, -7, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
581 ];
582 decimal_cases.iter().for_each(test_all_rounding::<10, 1>);
583 }
584
585 #[test]
586 fn test_from_ratio() {
587 #[rustfmt::skip]
588 fn test_all_rounding(
589 input: &(i32, i32, i32, Rounding, Rounding, Rounding, Rounding, Rounding, Rounding),
590 ) {
591 let (value, num, den, rnd_zero, rnd_away, rnd_up, rnd_down, rnd_halfeven, rnd_halfaway) = *input;
592 let (value, num, den) = (IBig::from(value), IBig::from(num), IBig::from(den));
593 assert_eq!(Zero::round_ratio(&value, num.clone(), &den), rnd_zero);
594 assert_eq!(Away::round_ratio(&value, num.clone(), &den), rnd_away);
595 assert_eq!(Up::round_ratio(&value, num.clone(), &den), rnd_up);
596 assert_eq!(Down::round_ratio(&value, num.clone(), &den), rnd_down);
597 assert_eq!(HalfEven::round_ratio(&value, num.clone(), &den), rnd_halfeven);
598 assert_eq!(HalfAway::round_ratio(&value, num, &den), rnd_halfaway);
599 }
600
601 #[rustfmt::skip]
603 let test_cases = [
604 ( 0, 0, 2, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
607 ( 0, 1, 2, NoOp , AddOne, AddOne, NoOp , NoOp , AddOne),
608 ( 0, -1, 2, NoOp , SubOne, NoOp , SubOne, NoOp , SubOne),
609 ( 0, 0, -2, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
610 ( 0, 1, -2, NoOp , SubOne, NoOp , SubOne, NoOp , SubOne),
611 ( 0, -1, -2, NoOp , AddOne, AddOne, NoOp , NoOp , AddOne),
612 ( 1, 0, 2, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
613 ( 1, 1, 2, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
614 ( 1, -1, 2, SubOne, NoOp , NoOp , SubOne, SubOne, NoOp ),
615 ( 1, 0, -2, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
616 ( 1, 1, -2, SubOne, NoOp , NoOp , SubOne, SubOne, NoOp ),
617 ( 1, -1, -2, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
618 (-1, 0, 2, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
619 (-1, 1, 2, AddOne, NoOp , AddOne, NoOp , AddOne, NoOp ),
620 (-1, -1, 2, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
621 (-1, 0, -2, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
622 (-1, 1, -2, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
623 (-1, -1, -2, AddOne, NoOp , AddOne, NoOp , AddOne, NoOp ),
624
625 ( 0, -2, 3, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
626 ( 0, -1, 3, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
627 ( 0, 0, 3, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
628 ( 0, 1, 3, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
629 ( 0, 2, 3, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
630 ( 0, -2, -3, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
631 ( 0, -1, -3, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
632 ( 0, 0, -3, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
633 ( 0, 1, -3, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
634 ( 0, 2, -3, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
635 ( 1, -2, 3, SubOne, NoOp , NoOp , SubOne, SubOne, SubOne),
636 ( 1, -1, 3, SubOne, NoOp , NoOp , SubOne, NoOp , NoOp ),
637 ( 1, 0, 3, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
638 ( 1, 1, 3, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
639 ( 1, 2, 3, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
640 ( 1, -2, -3, NoOp , AddOne, AddOne, NoOp , AddOne, AddOne),
641 ( 1, -1, -3, NoOp , AddOne, AddOne, NoOp , NoOp , NoOp ),
642 ( 1, 0, -3, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
643 ( 1, 1, -3, SubOne, NoOp , NoOp , SubOne, NoOp , NoOp ),
644 ( 1, 2, -3, SubOne, NoOp , NoOp , SubOne, SubOne, SubOne),
645 (-1, -2, 3, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
646 (-1, -1, 3, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
647 (-1, 0, 3, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
648 (-1, 1, 3, AddOne, NoOp , AddOne, NoOp , NoOp , NoOp ),
649 (-1, 2, 3, AddOne, NoOp , AddOne, NoOp , AddOne, AddOne),
650 (-1, -2, -3, AddOne, NoOp , AddOne, NoOp , AddOne, AddOne),
651 (-1, -1, -3, AddOne, NoOp , AddOne, NoOp , NoOp , NoOp ),
652 (-1, 0, -3, NoOp , NoOp , NoOp , NoOp , NoOp , NoOp ),
653 (-1, 1, -3, NoOp , SubOne, NoOp , SubOne, NoOp , NoOp ),
654 (-1, 2, -3, NoOp , SubOne, NoOp , SubOne, SubOne, SubOne),
655 ];
656 test_cases.iter().for_each(test_all_rounding);
657 }
658}