@@ -650,18 +650,51 @@ protected Object round(double n,
650
650
public abstract static class FloatRoundUpDecimalPrimitiveNode extends PrimitiveArrayArgumentsNode {
651
651
652
652
@ Specialization
653
- protected double roundNDecimal (double n , int ndigits ) {
653
+ protected double roundNDecimal (double n , int ndigits ,
654
+ @ Cached ConditionProfile boundaryCase ) {
654
655
long intPart = (long ) n ;
655
656
double s = Math .pow (10.0 , ndigits ) * Math .signum (n );
656
657
double f = (n % 1 ) * s ;
657
658
long fInt = (long ) f ;
658
659
double d = f % 1 ;
659
- if (d > 0.5 || Math .abs (n ) - Math .abs ((intPart + (fInt + 0.5 ) / s )) >= 0 ) {
660
+ int limit = Math .getExponent (n ) + Math .getExponent (s ) - 51 ;
661
+ if (boundaryCase .profile ((Math .getExponent (d ) <= limit ) ||
662
+ (Math .getExponent (1.0 - d ) <= limit ))) {
663
+ return findClosest (n , ndigits , s , d );
664
+ } else if (d > 0.5 || Math .abs (n ) - Math .abs ((intPart + (fInt + 0.5 ) / s )) >= 0 ) {
660
665
fInt += 1 ;
661
666
}
662
667
return intPart + fInt / s ;
663
668
}
669
+ }
664
670
671
+ /* If the rounding result is very near to an integer boundary then we need to find the number that is closest to the
672
+ * correct result. If we don't do this then it's possible to get errors in the least significant bit of the result.
673
+ * We'll test the adjacent double in the direction closest to the boundary and compare the fractional portions. If
674
+ * we're already at the minimum error we'll return the original number as it is already rounded as well as it can
675
+ * be. In the case of a tie we return the lower number, otherwise we check the go round again. */
676
+ private static double findClosest (double n , int ndigits , double s , double d ) {
677
+ double n2 ;
678
+ while (true ) {
679
+ if (d > 0.5 ) {
680
+ n2 = Math .nextAfter (n , n + s );
681
+ } else {
682
+ n2 = Math .nextAfter (n , n - s );
683
+ }
684
+ long intPart = (long ) n2 ;
685
+ double f = (n2 % 1 ) * s ;
686
+ long fInt = (long ) f ;
687
+ double d2 = f % 1 ;
688
+ int limit = Math .getExponent (n2 ) + Math .getExponent (s ) - 52 ;
689
+ if (((d > 0.5 ) ? 1 - d : d ) < ((d2 > 0.5 ) ? 1 - d2 : d2 )) {
690
+ return n ;
691
+ } else if (((d > 0.5 ) ? 1 - d : d ) == ((d2 > 0.5 ) ? 1 - d2 : d2 )) {
692
+ return Math .abs (n ) < Math .abs (n2 ) ? n : n2 ;
693
+ } else {
694
+ d = d2 ;
695
+ n = n2 ;
696
+ }
697
+ }
665
698
}
666
699
667
700
@ SuppressFBWarnings ("FE_FLOATING_POINT_EQUALITY" )
@@ -716,13 +749,18 @@ protected Object round(double n,
716
749
public abstract static class FloatRoundEvenDecimalPrimitiveNode extends PrimitiveArrayArgumentsNode {
717
750
718
751
@ Specialization
719
- protected double roundNDecimal (double n , int ndigits ) {
752
+ protected double roundNDecimal (double n , int ndigits ,
753
+ @ Cached ConditionProfile boundaryCase ) {
720
754
long intPart = (long ) n ;
721
755
double s = Math .pow (10.0 , ndigits ) * Math .signum (n );
722
756
double f = (n % 1 ) * s ;
723
757
long fInt = (long ) f ;
724
758
double d = f % 1 ;
725
- if (d > 0.5 ) {
759
+ int limit = Math .getExponent (n ) + Math .getExponent (s ) - 51 ;
760
+ if (boundaryCase .profile ((Math .getExponent (d ) <= limit ) ||
761
+ (Math .getExponent (1.0 - d ) <= limit ))) {
762
+ return findClosest (n , ndigits , s , d );
763
+ } else if (d > 0.5 ) {
726
764
fInt += 1 ;
727
765
} else if (d == 0.5 || Math .abs (n ) - Math .abs ((intPart + (fInt + 0.5 ) / s )) >= 0 ) {
728
766
fInt += fInt % 2 ;
@@ -775,13 +813,18 @@ protected Object round(double n,
775
813
public abstract static class FloatRoundDownDecimalPrimitiveNode extends PrimitiveArrayArgumentsNode {
776
814
777
815
@ Specialization
778
- protected double roundNDecimals (double n , int ndigits ) {
816
+ protected double roundNDecimal (double n , int ndigits ,
817
+ @ Cached ConditionProfile boundaryCase ) {
779
818
long intPart = (long ) n ;
780
819
double s = Math .pow (10.0 , ndigits ) * Math .signum (n );
781
820
double f = (n % 1 ) * s ;
782
821
long fInt = (long ) f ;
783
822
double d = f % 1 ;
784
- if (d > 0.5 && Math .abs (n ) - Math .abs ((intPart + (fInt + 0.5 ) / s )) > 0 ) {
823
+ int limit = Math .getExponent (n ) + Math .getExponent (s ) - 51 ;
824
+ if (boundaryCase .profile ((Math .getExponent (d ) <= limit ) ||
825
+ (Math .getExponent (1.0 - d ) <= limit ))) {
826
+ return findClosest (n , ndigits , s , d );
827
+ } else if (d > 0.5 && Math .abs (n ) - Math .abs ((intPart + (fInt + 0.5 ) / s )) > 0 ) {
785
828
fInt += 1 ;
786
829
}
787
830
return intPart + fInt / s ;
0 commit comments