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