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)]
64pub enum Rounding {
65 NoOp,
67
68 AddOne,
70
71 SubOne,
73}
74
75pub type Rounded<T> = Approximation<T, Rounding>;
80
81pub trait Round: Copy {
83 type Reverse: Round;
85
86 fn round_low_part<F: FnOnce() -> Ordering>(
89 integer: &IBig,
90 low_sign: Sign,
91 low_half_test: F,
92 ) -> Rounding;
93
94 #[inline]
97 fn round_fract<const B: Word>(integer: &IBig, fract: IBig, precision: usize) -> Rounding {
98 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 let (lb, ub) = fmag.log2_bounds();
109 let (b_lb, b_ub) = B.log2_bounds();
110
111 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 #[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
143pub trait ErrorBounds: Round {
145 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 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 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 Ordering::Less => Rounding::NoOp,
300 Ordering::Equal => {
302 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 Ordering::Greater => {
313 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(); 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 Ordering::Less => Rounding::NoOp,
359 Ordering::Equal => {
361 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 Ordering::Greater => {
373 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(); 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 #[rustfmt::skip]
459 let binary_cases = [
460 ( 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 #[rustfmt::skip]
488 let tenary_cases = [
489 ( 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 #[rustfmt::skip]
511 let decimal_cases = [
512 ( 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 #[rustfmt::skip]
557 let test_cases = [
558 ( 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}