The patch below is as submitted to OpenJDK bugzilla but with enhanced comments. 
It pertains to the correction loop in the doubleValue() method of 
FloatingDecimal. The situation appears to arise when the candidate value is 
less than 2*Double.MIN_NORMAL as for such values the ULP is less than 
2*Double.MIN_VALUE so that the intermediate result 0.5*ULP is less than 
Double.MIN_VALUE which rounds to zero per FP-strict and the correction is 
therefore zero. Thus the candidate value is unchanged. The fix is to add the 
ULP to twice the candidate value, obtain the intermediate result, and then 
halve it to obtain the adjusted candidate.

I am relatively new to IEEE-754 and this area of the code so any comments would 
be appreciated.

Thanks,

Brian

diff -r 1405ad6afb1e -r 36482ed9bb7e 
src/share/classes/sun/misc/FloatingDecimal.java
--- a/src/share/classes/sun/misc/FloatingDecimal.java   Thu Feb 14 11:09:07 
2013 -0800
+++ b/src/share/classes/sun/misc/FloatingDecimal.java   Thu Feb 14 16:47:53 
2013 -0800
@@ -35,8 +35,16 @@
     int         decExponent;
     char        digits[];
     int         nDigits;
+
+    // = ((doubleAsLongBits & expMask) >>> expShift) - expBias + 1 - 
bigIntNBits
+    // Set by doubleToBigInt().
     int         bigIntExp;
+
+    // Number of bits from the high order 1 bit to the low order 1 bit,
+    // inclusive, of the fractional (significand) part of a double.
+    // Set by doubleToBigInt().
     int         bigIntNBits;
+
     boolean     mustSetRoundDir = false;
     boolean     fromHex = false;
     int         roundDir = 0; // set by doubleValue
@@ -1604,7 +1612,50 @@
                 } else if ( cmpResult == 0 ){
                     // difference is exactly half an ULP
                     // round to some other value maybe, then finish
-                    dValue += 0.5*ulp( dValue, overvalue );
+                    // Fix of bug 4396272. This method has strictfp modifier 
now.
+                    // Nevertheless, the two comments below explain why it can 
work without strictfp too.
+                    //
+                    // In the logical expression tested on the if-block,
+                    // bigIntExp = expBiased - expBias + 1 - bigIntNBits so 
that
+                    // bigIntExp+bigIntNBits = expBiased - expBias + 1 so the 
inequality tested is
+                    // expBiased - expBias + 1 > -expBias+2 or expBiased > 1 
which implies
+                    // exUnbiased > 1 - expBias = -1022 or expUnbiased > -1021 
so that
+                    // old dValue > 2^(-1021) = 2*2^(-1022) = 
2*Double.MIN_NORMAL.
+                    // Either ">" or ">=" could be used in the logical 
expression
+                    // as both branches evaluate to the same result when 
equality obtains.
+                    //
+                    // As dValue becomes smaller, the ULP descreases reaching
+                    // Double.MIN_VALUE for dValue == 2*Double.MIN_NORMAL. 
Below
+                    // this the intermediate result 0.5*ULP will be rounded to
+                    // zero per FP-strict. As a result no correction to dValue
+                    // will occur. To prevent this, 2*dValue + ULP is first
+                    // computed so that the effect of adding half the ULP is 
not
+                    // lost, then this intermediate result is halved.
+                    //
+                    if ( bigIntExp+bigIntNBits > -expBias+2 ) {
+                        // Here old dValue > 2*Double.MIN_NORMAL
+                        //
+                        // Non-FP-strict case:
+                        // If overvalue == false then ulp(dValue,false) >= 
2*Double.MIN_VALUE and 0.5*ulp is exact.
+                        // If overvalue == true and dValue > 
2*Double.MIN_NORMAL then ulp(dValue,true) <= -2*Double.MIN_VALUE and 0.5*ulp is 
exact.
+                        // If overvalue == true and dValue == 
2*Double.MIN_NORMAL then ulp(dValue,true) == -Double.MIN_VALUE.
+                        //   Hence 0.5*ulp is rounded to 0 in double value set 
and 0.5*ulp is exact in double-extended-exponent value set.
+                        //   In both value sets new dValue is rounded to 
2*Double.MIN_NOPMAL as expected
+                        dValue += 0.5*ulp( dValue, overvalue );
+                    } else {
+                        // Here old dValue <= 2*Double.MIN_NORMAL
+                        //
+                        // Non-FP-strict case:
+                        // If overvalue == false then ulp(dValue,false) == 
Double.MIN_VALUE
+                        //   If dValue >= Double.MIN_NORMAL then dValue*2 + 
ulp rounds to nearest even and 0.5*(...) is exact
+                        //   If dValue < Double.MIN_NORMAL then dValue*2 + ulp 
is exact and 0.5*(...) rounds to nearest even in double value set
+                        //     and is exact in double-extended-exponent value 
set and then rounds to nearest even double before storing to new dValue. 
+                        // If overvalue == true then ulp(dValue,true) == 
-Double.MIN_VALUE
+                        //   If dValue > Double.MIN_NORMAL then dValue*2 + ulp 
rounds to nearest even and 0.5*(...) is exact
+                        //   If dValue <= Double.MIN_NORMAL then dValue*2 + 
ulp is exact and 0.5*(...) rounds to nearest even in double value set
+                        //     and is exact in double-extended-exponent value 
set and then rounds to nearest even double before storing to new dValue. 
+                       dValue = 0.5*(dValue*2 + ulp( dValue, overvalue));
+                    }
                     // should check for bigIntNBits == 1 here??
                     if (mustSetRoundDir) {
                         roundDir = overvalue ? -1 : 1;
diff -r 1405ad6afb1e -r 36482ed9bb7e test/java/lang/Double/ParseDouble.java
--- a/test/java/lang/Double/ParseDouble.java    Thu Feb 14 11:09:07 2013 -0800
+++ b/test/java/lang/Double/ParseDouble.java    Thu Feb 14 16:47:53 2013 -0800
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 4160406 4705734 4707389 4826774 4895911 4421494 7021568 7039369
+ * @bug 4160406 4705734 4707389 4826774 4895911 4421494 7021568 7039369 4396272
  * @summary Test for Double.parseDouble method and acceptance regex
  */
 
@@ -560,17 +560,18 @@
      * region that should convert to that value.
      */
     private static void testSubnormalPowers() {
+        boolean failed = false;
         BigDecimal TWO = BigDecimal.valueOf(2);
         // An ulp is the same for all subnormal values
         BigDecimal ulp_BD = new BigDecimal(Double.MIN_VALUE);
 
-        // Test subnormal powers of two
-        for(int i = -1074; i <= -1022; i++) {
+        // Test subnormal powers of two (except Double.MIN_VALUE)
+        for(int i = -1073; i <= -1022; i++) {
             double d = Math.scalb(1.0, i);
 
             /*
              * The region [d - ulp/2, d + ulp/2] should round to d.
-             */
+            */
             BigDecimal d_BD = new BigDecimal(d);
 
             BigDecimal lowerBound = d_BD.subtract(ulp_BD.divide(TWO));
@@ -578,17 +579,52 @@
 
             double convertedLowerBound = 
Double.parseDouble(lowerBound.toString());
             double convertedUpperBound = 
Double.parseDouble(upperBound.toString());
+            if (convertedLowerBound != d) {
+                failed = true;
+                System.out.printf("2^%d lowerBound converts as %a %s%n",
+                                  i, convertedLowerBound, lowerBound);
+            }
+            if (convertedUpperBound != d) {
+                failed = true;
+                System.out.printf("2^%d upperBound converts as %a %s%n",
+                                  i, convertedUpperBound, upperBound);
+            }
         }
+        /*
+         * Double.MIN_VALUE
+         * The region ]0.5*Double.MIN_VALUE, 1.5*Double.MIN_VALUE[ should 
round to Double.MIN_VALUE .
+         */
+        BigDecimal minValue = new BigDecimal(Double.MIN_VALUE);
+        if (Double.parseDouble(minValue.multiply(new 
BigDecimal(0.5)).toString()) != 0.0) {
+            failed = true;
+            System.out.printf("0.5*MIN_VALUE doesn't convert 0%n");
+        }
+        if (Double.parseDouble(minValue.multiply(new 
BigDecimal(0.50000000001)).toString()) != Double.MIN_VALUE) {
+            failed = true;
+            System.out.printf("0.50000000001*MIN_VALUE doesn't convert to 
MIN_VALUE%n");
+        }
+        if (Double.parseDouble(minValue.multiply(new 
BigDecimal(1.49999999999)).toString()) != Double.MIN_VALUE) {
+            failed = true;
+            System.out.printf("1.49999999999*MIN_VALUE doesn't convert to 
MIN_VALUE%n");
+        }
+        if (Double.parseDouble(minValue.multiply(new 
BigDecimal(1.5)).toString()) != 2*Double.MIN_VALUE) {
+            failed = true;
+            System.out.printf("1.5*MIN_VALUE doesn't convert to 
2*MIN_VALUE%n");
+        }
+
+        if (failed)
+            throw new RuntimeException("Inconsistent conversion");
     }
 
 
     private static void testStrictness() {
-        final double expected = 0x0.0000008000001p-1022;
+        final double expected = 0x0.0000008000000p-1022;
+//        final double expected = 0x0.0000008000001p-1022;
         boolean failed = false;
         double conversion = 0.0;
         double sum = 0.0; // Prevent conversion from being optimized away
 
-        //2^-1047 + 2^-1075
+        //2^-1047 + 2^-1075 rounds to 2^-1047
         String decimal = 
"6.631236871469758276785396630275967243399099947355303144249971758736286630139265439618068200788048744105960420552601852889715006376325666595539603330361800519107591783233358492337208057849499360899425128640718856616503093444922854759159988160304439909868291973931426625698663157749836252274523485312442358651207051292453083278116143932569727918709786004497872322193856150225415211997283078496319412124640111777216148110752815101775295719811974338451936095907419622417538473679495148632480391435931767981122396703443803335529756003353209830071832230689201383015598792184172909927924176339315507402234836120730914783168400715462440053817592702766213559042115986763819482654128770595766806872783349146967171293949598850675682115696218943412532098591327667236328125E-316";
 
         for(int i = 0; i <= 12_000; i++) {

Reply via email to