@@ -2,6 +2,10 @@ import {
2
2
countFractionalDigits ,
3
3
countSignificantDigits ,
4
4
Digit ,
5
+ ROUNDING_MODE_CEILING ,
6
+ ROUNDING_MODE_FLOOR ,
7
+ ROUNDING_MODE_HALF_EVEN ,
8
+ ROUNDING_MODE_HALF_EXPAND ,
5
9
ROUNDING_MODE_TRUNCATE ,
6
10
RoundingMode ,
7
11
} from "./common.mjs" ;
@@ -411,6 +415,83 @@ export class Rational {
411
415
return ( this . isNegative ? "-" : "" ) + result ;
412
416
}
413
417
418
+ private static roundHalfEven (
419
+ initialPart : Rational ,
420
+ penultimateDigit : Digit ,
421
+ finalDigit : Digit ,
422
+ quantum : Rational
423
+ ) : Rational {
424
+ if ( finalDigit < 5 ) {
425
+ return initialPart ;
426
+ }
427
+
428
+ if ( finalDigit > 5 ) {
429
+ return Rational . add (
430
+ initialPart ,
431
+ initialPart . isNegative ? quantum . negate ( ) : quantum
432
+ ) ;
433
+ }
434
+
435
+ if ( penultimateDigit % 2 === 0 ) {
436
+ return initialPart ;
437
+ }
438
+
439
+ return Rational . add (
440
+ initialPart ,
441
+ initialPart . isNegative ? quantum . negate ( ) : quantum
442
+ ) ;
443
+ }
444
+
445
+ private static roundHalfExpand (
446
+ initialPart : Rational ,
447
+ penultimateDigit : Digit ,
448
+ finalDigit : Digit ,
449
+ quantum : Rational
450
+ ) : Rational {
451
+ if ( finalDigit < 5 ) {
452
+ return initialPart ;
453
+ }
454
+
455
+ return Rational . add (
456
+ initialPart ,
457
+ initialPart . isNegative ? quantum . negate ( ) : quantum
458
+ ) ;
459
+ }
460
+
461
+ private static roundCeil (
462
+ initialPart : Rational ,
463
+ penultimateDigit : Digit ,
464
+ finalDigit : Digit ,
465
+ quantum : Rational
466
+ ) : Rational {
467
+ if ( initialPart . isNegative ) {
468
+ return initialPart ;
469
+ }
470
+
471
+ if ( finalDigit === 0 ) {
472
+ return initialPart ;
473
+ }
474
+
475
+ return Rational . add ( initialPart , quantum ) ;
476
+ }
477
+
478
+ private static roundFloor (
479
+ initialPart : Rational ,
480
+ penultimateDigit : Digit ,
481
+ finalDigit : Digit ,
482
+ quantum : Rational
483
+ ) : Rational {
484
+ if ( initialPart . isNegative ) {
485
+ if ( finalDigit === 0 ) {
486
+ return initialPart ;
487
+ }
488
+
489
+ return Rational . subtract ( initialPart , quantum ) ;
490
+ }
491
+
492
+ return initialPart ;
493
+ }
494
+
414
495
round ( numFractionalDigits : number , mode : RoundingMode ) : Rational {
415
496
if ( ! Number . isInteger ( numFractionalDigits ) ) {
416
497
throw new TypeError (
@@ -424,43 +505,69 @@ export class Rational {
424
505
) ;
425
506
}
426
507
427
- if ( numFractionalDigits > 1 ) {
428
- return this . scale10 ( 1 )
429
- . round ( numFractionalDigits - 1 , mode )
430
- . scale10 ( - 1 ) ;
431
- }
432
- let s = this . toFixed ( 1 ) ;
508
+ let s = this . toFixed ( numFractionalDigits + 1 ) ;
433
509
434
510
let [ integerPart , fractionalPart ] = s . split ( "." ) ;
435
511
436
- if ( mode === ROUNDING_MODE_TRUNCATE ) {
437
- return Rational . fromString ( integerPart ) ;
438
- }
512
+ let quantum = Rational . fromString (
513
+ numFractionalDigits === 0
514
+ ? "1"
515
+ : "0" + "." + "0" . repeat ( numFractionalDigits - 1 ) + "1"
516
+ ) ;
517
+ let truncated = Rational . fromString (
518
+ integerPart + "." + fractionalPart . substring ( 0 , numFractionalDigits )
519
+ ) ;
439
520
440
521
let penultimateDigit = parseInt (
441
- integerPart . charAt ( integerPart . length - 1 )
522
+ numFractionalDigits === 0
523
+ ? integerPart . charAt ( integerPart . length - 1 )
524
+ : fractionalPart . charAt ( numFractionalDigits - 1 )
525
+ ) as Digit ;
526
+ let finalDigit = parseInt (
527
+ fractionalPart . charAt ( numFractionalDigits )
442
528
) as Digit ;
443
- let finalDigit = parseInt ( fractionalPart . charAt ( 0 ) ) as Digit ;
444
529
445
- if ( finalDigit < 5 ) {
446
- return Rational . fromString ( integerPart ) ;
530
+ if ( mode === ROUNDING_MODE_TRUNCATE ) {
531
+ return truncated ;
447
532
}
448
533
449
- if ( finalDigit > 5 ) {
450
- return Rational . add (
451
- Rational . fromString ( integerPart ) ,
452
- Rational . fromString ( "1" )
534
+ if ( mode === ROUNDING_MODE_HALF_EVEN ) {
535
+ return Rational . roundHalfEven (
536
+ truncated ,
537
+ penultimateDigit ,
538
+ finalDigit ,
539
+ quantum
453
540
) ;
454
541
}
455
542
456
- if ( penultimateDigit % 2 === 0 ) {
457
- return Rational . fromString ( integerPart ) ;
543
+ if ( mode === ROUNDING_MODE_CEILING ) {
544
+ return Rational . roundCeil (
545
+ truncated ,
546
+ penultimateDigit ,
547
+ finalDigit ,
548
+ quantum
549
+ ) ;
458
550
}
459
551
460
- return Rational . add (
461
- Rational . fromString ( integerPart ) ,
462
- Rational . fromString ( "1" )
463
- ) ;
552
+ if ( mode === ROUNDING_MODE_FLOOR ) {
553
+ return Rational . roundFloor (
554
+ truncated ,
555
+ penultimateDigit ,
556
+ finalDigit ,
557
+ quantum
558
+ ) ;
559
+ }
560
+
561
+ if ( mode === ROUNDING_MODE_HALF_EXPAND ) {
562
+ return Rational . roundHalfExpand (
563
+ truncated ,
564
+ penultimateDigit ,
565
+ finalDigit ,
566
+ quantum
567
+ ) ;
568
+ }
569
+
570
+ throw new RangeError ( "Unsupported rounding mode: " + mode ) ;
464
571
}
465
572
466
573
cmp ( x : Rational ) : - 1 | 0 | 1 {
0 commit comments