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