From c0127d8531be1aac982bc65ff316eb8f43c1fa6f Mon Sep 17 00:00:00 2001 From: bpb Date: Fri, 21 Jun 2013 11:12:18 -0700 Subject: [PATCH] 7192954: Fix Float.parseFloat to round correctly and preserve monotonicity. 4396272: Parsing doubles fails to follow IEEE for largest decimal that should yield 0 7039391: Use Math.ulp in FloatingDecimal Summary: Correct rounding and monotonicity problems in floats and doubles Reviewed-by: bpb, martin Contributed-by: Dmitry Nadezhin , Louis Wasserman --- src/share/classes/sun/misc/FDBigInteger.java | 4 +- .../classes/sun/misc/FloatingDecimal.java | 505 ++++++++++-------- test/java/lang/Double/ParseDouble.java | 157 +++++- test/java/lang/Float/ParseFloat.java | 126 ++++- .../FloatingDecimal/TestFDBigInteger.java | 8 + 5 files changed, 552 insertions(+), 248 deletions(-) diff --git a/src/share/classes/sun/misc/FDBigInteger.java b/src/share/classes/sun/misc/FDBigInteger.java index d575c350b..77d6fbc08 100644 --- a/src/share/classes/sun/misc/FDBigInteger.java +++ b/src/share/classes/sun/misc/FDBigInteger.java @@ -782,7 +782,7 @@ public /*@ spec_bigint_math @*/ class FDBigInteger { assert this.size() >= subtrahend.size() : "result should be positive"; FDBigInteger minuend; if (this.isImmutable) { - minuend = new FDBigInteger(this.data, this.offset); + minuend = new FDBigInteger(this.data.clone(), this.offset); } else { minuend = this; } @@ -851,7 +851,7 @@ public /*@ spec_bigint_math @*/ class FDBigInteger { assert this.size() >= subtrahend.size() : "result should be positive"; FDBigInteger minuend = this; if (subtrahend.isImmutable) { - subtrahend = new FDBigInteger(subtrahend.data, subtrahend.offset); + subtrahend = new FDBigInteger(subtrahend.data.clone(), subtrahend.offset); } int offsetDiff = minuend.offset - subtrahend.offset; int[] sData = subtrahend.data; diff --git a/src/share/classes/sun/misc/FloatingDecimal.java b/src/share/classes/sun/misc/FloatingDecimal.java index f2fe492c4..0daaaadc4 100644 --- a/src/share/classes/sun/misc/FloatingDecimal.java +++ b/src/share/classes/sun/misc/FloatingDecimal.java @@ -49,12 +49,14 @@ public class FloatingDecimal{ static final int MAX_DECIMAL_EXPONENT = 308; static final int MIN_DECIMAL_EXPONENT = -324; static final int BIG_DECIMAL_EXPONENT = 324; // i.e. abs(MIN_DECIMAL_EXPONENT) + static final int MAX_NDIGITS = 1100; static final int SINGLE_EXP_SHIFT = FloatConsts.SIGNIFICAND_WIDTH - 1; static final int SINGLE_FRACT_HOB = 1<ASCIIToBinaryConverter. @@ -1038,7 +1036,6 @@ public class FloatingDecimal{ int decExponent; char digits[]; int nDigits; - int roundDir = 0; // set by doubleValue ASCIIToBinaryBuffer( boolean negSign, int decExponent, char[] digits, int n) { @@ -1048,41 +1045,7 @@ public class FloatingDecimal{ this.nDigits = n; } - @Override - public double doubleValue() { - return doubleValue(false); - } - - /** - * Computes a number that is the ULP of the given value, - * for purposes of addition/subtraction. Generally easy. - * More difficult if subtracting and the argument - * is a normalized a power of 2, as the ULP changes at these points. - */ - private static double ulp(double dval, boolean subtracting) { - long lbits = Double.doubleToLongBits(dval) & ~DoubleConsts.SIGN_BIT_MASK; - int binexp = (int) (lbits >>> EXP_SHIFT); - double ulpval; - if (subtracting && (binexp >= EXP_SHIFT) && ((lbits & DoubleConsts.SIGNIF_BIT_MASK) == 0L)) { - // for subtraction from normalized, powers of 2, - // use next-smaller exponent - binexp -= 1; - } - if (binexp > EXP_SHIFT) { - ulpval = Double.longBitsToDouble(((long) (binexp - EXP_SHIFT)) << EXP_SHIFT); - } else if (binexp == 0) { - ulpval = Double.MIN_VALUE; - } else { - ulpval = Double.longBitsToDouble(1L << (binexp - 1)); - } - if (subtracting) { - ulpval = -ulpval; - } - - return ulpval; - } - - /** + /* * Takes a FloatingDecimal, which we presumably just scanned in, * and finds out what its value is, as a double. * @@ -1090,15 +1053,9 @@ public class FloatingDecimal{ * ROUNDING DIRECTION in case the result is really destined * for a single-precision float. */ - private strictfp double doubleValue(boolean mustSetRoundDir) { + @Override + public double doubleValue() { int kDigits = Math.min(nDigits, MAX_DECIMAL_DIGITS + 1); - long lValue; - double dValue; - double rValue; - - if (mustSetRoundDir) { - roundDir = 0; - } // // convert the lead kDigits to a long integer. // @@ -1108,11 +1065,11 @@ public class FloatingDecimal{ for (int i = 1; i < iDigits; i++) { iValue = iValue * 10 + (int) digits[i] - (int) '0'; } - lValue = (long) iValue; + long lValue = (long) iValue; for (int i = iDigits; i < kDigits; i++) { lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0'); } - dValue = (double) lValue; + double dValue = (double) lValue; int exp = decExponent - kDigits; // // lValue now contains a long integer with the value of @@ -1140,13 +1097,7 @@ public class FloatingDecimal{ // Can get the answer with one operation, // thus one roundoff. // - rValue = dValue * SMALL_10_POW[exp]; - if (mustSetRoundDir) { - double tValue = rValue / SMALL_10_POW[exp]; - roundDir = (tValue == dValue) ? 0 - : (tValue < dValue) ? 1 - : -1; - } + double rValue = dValue * SMALL_10_POW[exp]; return (isNegative) ? -rValue : rValue; } int slop = MAX_DECIMAL_DIGITS - kDigits; @@ -1158,14 +1109,7 @@ public class FloatingDecimal{ // with one rounding. // dValue *= SMALL_10_POW[slop]; - rValue = dValue * SMALL_10_POW[exp - slop]; - - if (mustSetRoundDir) { - double tValue = rValue / SMALL_10_POW[exp - slop]; - roundDir = (tValue == dValue) ? 0 - : (tValue < dValue) ? 1 - : -1; - } + double rValue = dValue * SMALL_10_POW[exp - slop]; return (isNegative) ? -rValue : rValue; } // @@ -1176,13 +1120,7 @@ public class FloatingDecimal{ // // Can get the answer in one division. // - rValue = dValue / SMALL_10_POW[-exp]; - if (mustSetRoundDir) { - double tValue = rValue * SMALL_10_POW[-exp]; - roundDir = (tValue == dValue) ? 0 - : (tValue < dValue) ? 1 - : -1; - } + double rValue = dValue / SMALL_10_POW[-exp]; return (isNegative) ? -rValue : rValue; } // @@ -1303,9 +1241,14 @@ public class FloatingDecimal{ // Formulate the EXACT big-number result as // bigD0 * 10^exp // + if (nDigits > MAX_NDIGITS) { + nDigits = MAX_NDIGITS + 1; + digits[MAX_NDIGITS] = '1'; + } FDBigInteger bigD0 = new FDBigInteger(lValue, digits, kDigits, nDigits); exp = decExponent - nDigits; + long ieeeBits = Double.doubleToRawLongBits(dValue); // IEEE-754 bits of double candidate final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop bigD0 = bigD0.multByPow52(D5, 0); @@ -1315,10 +1258,9 @@ public class FloatingDecimal{ correctionLoop: while (true) { - // here dValue can't be NaN, Infinity or zero - long bigBbits = Double.doubleToRawLongBits(dValue) & ~DoubleConsts.SIGN_BIT_MASK; - int binexp = (int) (bigBbits >>> EXP_SHIFT); - bigBbits &= DoubleConsts.SIGNIF_BIT_MASK; + // here ieeeBits can't be NaN, Infinity or zero + int binexp = (int) (ieeeBits >>> EXP_SHIFT); + long bigBbits = ieeeBits & DoubleConsts.SIGNIF_BIT_MASK; if (binexp > 0) { bigBbits |= FRACT_HOB; } else { // Normalize denormalized numbers. @@ -1358,7 +1300,7 @@ public class FloatingDecimal{ if (binexp <= -DoubleConsts.EXP_BIAS) { // This is going to be a denormalized number // (if not actually zero). - // half an ULP is at 2^-(expBias+EXP_SHIFT+1) + // half an ULP is at 2^-(DoubleConsts.EXP_BIAS+EXP_SHIFT+1) hulpbias = binexp + lowOrderZeros + DoubleConsts.EXP_BIAS; } else { hulpbias = 1 + lowOrderZeros; @@ -1422,17 +1364,12 @@ public class FloatingDecimal{ if ((cmpResult) < 0) { // difference is small. // this is close enough - if (mustSetRoundDir) { - roundDir = overvalue ? -1 : 1; - } break correctionLoop; } else if (cmpResult == 0) { // difference is exactly half an ULP // round to some other value maybe, then finish - dValue += 0.5 * ulp(dValue, overvalue); - // should check for bigIntNBits == 1 here?? - if (mustSetRoundDir) { - roundDir = overvalue ? -1 : 1; + if ((ieeeBits & 1) != 0) { // half ties to even + ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp } break correctionLoop; } else { @@ -1440,15 +1377,18 @@ public class FloatingDecimal{ // could scale addend by ratio of difference to // halfUlp here, if we bothered to compute that difference. // Most of the time ( I hope ) it is about 1 anyway. - dValue += ulp(dValue, overvalue); - if (dValue == 0.0 || dValue == Double.POSITIVE_INFINITY) { + ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp + if (ieeeBits == 0 || ieeeBits == DoubleConsts.EXP_BIT_MASK) { // 0.0 or Double.POSITIVE_INFINITY break correctionLoop; // oops. Fell off end of range. } continue; // try again. } } - return (isNegative) ? -dValue : dValue; + if (isNegative) { + ieeeBits |= DoubleConsts.SIGN_BIT_MASK; + } + return Double.longBitsToDouble(ieeeBits); } /** @@ -1461,18 +1401,16 @@ public class FloatingDecimal{ * ( because of the preference to a zero low-order bit ). */ @Override - public strictfp float floatValue() { + public float floatValue() { int kDigits = Math.min(nDigits, SINGLE_MAX_DECIMAL_DIGITS + 1); - int iValue; - float fValue; // // convert the lead kDigits to an integer. // - iValue = (int) digits[0] - (int) '0'; + int iValue = (int) digits[0] - (int) '0'; for (int i = 1; i < kDigits; i++) { iValue = iValue * 10 + (int) digits[i] - (int) '0'; } - fValue = (float) iValue; + float fValue = (float) iValue; int exp = decExponent - kDigits; // // iValue now contains an integer with the value of @@ -1505,7 +1443,7 @@ public class FloatingDecimal{ int slop = SINGLE_MAX_DECIMAL_DIGITS - kDigits; if (exp <= SINGLE_MAX_SMALL_TEN + slop) { // - // We can multiply dValue by 10^(slop) + // We can multiply fValue by 10^(slop) // and it is still "small" and exact. // Then we can multiply by 10^(exp-slop) // with one rounding. @@ -1555,38 +1493,208 @@ public class FloatingDecimal{ // The sum of digits plus exponent is greater than // what we think we can do with one error. // - // Start by weeding out obviously out-of-range - // results, then convert to double and go to - // common hard-case code. + // Start by approximating the right answer by, + // naively, scaling by powers of 10. + // Scaling uses doubles to avoid overflow/underflow. + // + double dValue = fValue; + if (exp > 0) { + if (decExponent > SINGLE_MAX_DECIMAL_EXPONENT + 1) { + // + // Lets face it. This is going to be + // Infinity. Cut to the chase. + // + return (isNegative) ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY; + } + if ((exp & 15) != 0) { + dValue *= SMALL_10_POW[exp & 15]; + } + if ((exp >>= 4) != 0) { + int j; + for (j = 0; exp > 0; j++, exp >>= 1) { + if ((exp & 1) != 0) { + dValue *= BIG_10_POW[j]; + } + } + } + } else if (exp < 0) { + exp = -exp; + if (decExponent < SINGLE_MIN_DECIMAL_EXPONENT - 1) { + // + // Lets face it. This is going to be + // zero. Cut to the chase. + // + return (isNegative) ? -0.0f : 0.0f; + } + if ((exp & 15) != 0) { + dValue /= SMALL_10_POW[exp & 15]; + } + if ((exp >>= 4) != 0) { + int j; + for (j = 0; exp > 0; j++, exp >>= 1) { + if ((exp & 1) != 0) { + dValue *= TINY_10_POW[j]; + } + } + } + } + fValue = Math.max(Float.MIN_VALUE, Math.min(Float.MAX_VALUE, (float) dValue)); + + // + // fValue is now approximately the result. + // The hard part is adjusting it, by comparison + // with FDBigInteger arithmetic. + // Formulate the EXACT big-number result as + // bigD0 * 10^exp // - if (decExponent > SINGLE_MAX_DECIMAL_EXPONENT + 1) { + if (nDigits > SINGLE_MAX_NDIGITS) { + nDigits = SINGLE_MAX_NDIGITS + 1; + digits[SINGLE_MAX_NDIGITS] = '1'; + } + FDBigInteger bigD0 = new FDBigInteger(iValue, digits, kDigits, nDigits); + exp = decExponent - nDigits; + + int ieeeBits = Float.floatToRawIntBits(fValue); // IEEE-754 bits of float candidate + final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop + final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop + bigD0 = bigD0.multByPow52(D5, 0); + bigD0.makeImmutable(); // prevent bigD0 modification inside correctionLoop + FDBigInteger bigD = null; + int prevD2 = 0; + + correctionLoop: + while (true) { + // here ieeeBits can't be NaN, Infinity or zero + int binexp = ieeeBits >>> SINGLE_EXP_SHIFT; + int bigBbits = ieeeBits & FloatConsts.SIGNIF_BIT_MASK; + if (binexp > 0) { + bigBbits |= SINGLE_FRACT_HOB; + } else { // Normalize denormalized numbers. + assert bigBbits != 0 : bigBbits; // floatToBigInt(0.0) + int leadingZeros = Integer.numberOfLeadingZeros(bigBbits); + int shift = leadingZeros - (31 - SINGLE_EXP_SHIFT); + bigBbits <<= shift; + binexp = 1 - shift; + } + binexp -= FloatConsts.EXP_BIAS; + int lowOrderZeros = Integer.numberOfTrailingZeros(bigBbits); + bigBbits >>>= lowOrderZeros; + final int bigIntExp = binexp - SINGLE_EXP_SHIFT + lowOrderZeros; + final int bigIntNBits = SINGLE_EXP_SHIFT + 1 - lowOrderZeros; + + // + // Scale bigD, bigB appropriately for + // big-integer operations. + // Naively, we multiply by powers of ten + // and powers of two. What we actually do + // is keep track of the powers of 5 and + // powers of 2 we would use, then factor out + // common divisors before doing the work. // - // Lets face it. This is going to be - // Infinity. Cut to the chase. + int B2 = B5; // powers of 2 in bigB + int D2 = D5; // powers of 2 in bigD + int Ulp2; // powers of 2 in halfUlp. + if (bigIntExp >= 0) { + B2 += bigIntExp; + } else { + D2 -= bigIntExp; + } + Ulp2 = B2; + // shift bigB and bigD left by a number s. t. + // halfUlp is still an integer. + int hulpbias; + if (binexp <= -FloatConsts.EXP_BIAS) { + // This is going to be a denormalized number + // (if not actually zero). + // half an ULP is at 2^-(FloatConsts.EXP_BIAS+SINGLE_EXP_SHIFT+1) + hulpbias = binexp + lowOrderZeros + FloatConsts.EXP_BIAS; + } else { + hulpbias = 1 + lowOrderZeros; + } + B2 += hulpbias; + D2 += hulpbias; + // if there are common factors of 2, we might just as well + // factor them out, as they add nothing useful. + int common2 = Math.min(B2, Math.min(D2, Ulp2)); + B2 -= common2; + D2 -= common2; + Ulp2 -= common2; + // do multiplications by powers of 5 and 2 + FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2); + if (bigD == null || prevD2 != D2) { + bigD = bigD0.leftShift(D2); + prevD2 = D2; + } // - return (isNegative) ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY; - } else if (decExponent < SINGLE_MIN_DECIMAL_EXPONENT - 1) { + // to recap: + // bigB is the scaled-big-int version of our floating-point + // candidate. + // bigD is the scaled-big-int version of the exact value + // as we understand it. + // halfUlp is 1/2 an ulp of bigB, except for special cases + // of exact powers of 2 // - // Lets face it. This is going to be - // zero. Cut to the chase. + // the plan is to compare bigB with bigD, and if the difference + // is less than halfUlp, then we're satisfied. Otherwise, + // use the ratio of difference to halfUlp to calculate a fudge + // factor to add to the floating value, then go 'round again. // - return (isNegative) ? -0.0f : 0.0f; - } + FDBigInteger diff; + int cmpResult; + boolean overvalue; + if ((cmpResult = bigB.cmp(bigD)) > 0) { + overvalue = true; // our candidate is too big. + diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse + if ((bigIntNBits == 1) && (bigIntExp > -FloatConsts.EXP_BIAS + 1)) { + // candidate is a normalized exact power of 2 and + // is too big (larger than Float.MIN_NORMAL). We will be subtracting. + // For our purposes, ulp is the ulp of the + // next smaller range. + Ulp2 -= 1; + if (Ulp2 < 0) { + // rats. Cannot de-scale ulp this far. + // must scale diff in other direction. + Ulp2 = 0; + diff = diff.leftShift(1); + } + } + } else if (cmpResult < 0) { + overvalue = false; // our candidate is too small. + diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse + } else { + // the candidate is exactly right! + // this happens with surprising frequency + break correctionLoop; + } + cmpResult = diff.cmpPow52(B5, Ulp2); + if ((cmpResult) < 0) { + // difference is small. + // this is close enough + break correctionLoop; + } else if (cmpResult == 0) { + // difference is exactly half an ULP + // round to some other value maybe, then finish + if ((ieeeBits & 1) != 0) { // half ties to even + ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp + } + break correctionLoop; + } else { + // difference is non-trivial. + // could scale addend by ratio of difference to + // halfUlp here, if we bothered to compute that difference. + // Most of the time ( I hope ) it is about 1 anyway. + ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp + if (ieeeBits == 0 || ieeeBits == FloatConsts.EXP_BIT_MASK) { // 0.0 or Float.POSITIVE_INFINITY + break correctionLoop; // oops. Fell off end of range. + } + continue; // try again. + } - // - // Here, we do 'way too much work, but throwing away - // our partial results, and going and doing the whole - // thing as double, then throwing away half the bits that computes - // when we convert back to float. - // - // The alternative is to reproduce the whole multiple-precision - // algorithm for float precision, or to try to parameterize it - // for common usage. The former will take about 400 lines of code, - // and the latter I tried without success. Thus the semi-hack - // answer here. - // - double dValue = doubleValue(true); - return stickyRound(dValue, roundDir); + } + if (isNegative) { + ieeeBits |= FloatConsts.SIGN_BIT_MASK; + } + return Float.intBitsToFloat(ieeeBits); } @@ -1935,32 +2043,6 @@ public class FloatingDecimal{ throw new NumberFormatException("For input string: \"" + in + "\""); } - /** - * Rounds a double to a float. - * In addition to the fraction bits of the double, - * look at the class instance variable roundDir, - * which should help us avoid double-rounding error. - * roundDir was set in hardValueOf if the estimate was - * close enough, but not exact. It tells us which direction - * of rounding is preferred. - */ - static float stickyRound( double dval, int roundDirection ){ - if(roundDirection!=0) { - long lbits = Double.doubleToRawLongBits( dval ); - long binexp = lbits & DoubleConsts.EXP_BIT_MASK; - if ( binexp == 0L || binexp == DoubleConsts.EXP_BIT_MASK ){ - // what we have here is special. - // don't worry, the right thing will happen. - return (float) dval; - } - lbits += (long)roundDirection; // hack-o-matic. - return (float)Double.longBitsToDouble( lbits ); - } else { - return (float)dval; - } - } - - private static class HexFloatPattern { /** * Grammar is compatible with hexadecimal floating-point constants @@ -2282,6 +2364,39 @@ public class FloatingDecimal{ // else all of string was seen, round and sticky are // correct as false. + // Float calculations + int floatBits = isNegative ? FloatConsts.SIGN_BIT_MASK : 0; + if (exponent >= FloatConsts.MIN_EXPONENT) { + if (exponent > FloatConsts.MAX_EXPONENT) { + // Float.POSITIVE_INFINITY + floatBits |= FloatConsts.EXP_BIT_MASK; + } else { + int threshShift = DoubleConsts.SIGNIFICAND_WIDTH - FloatConsts.SIGNIFICAND_WIDTH - 1; + boolean floatSticky = (significand & ((1L << threshShift) - 1)) != 0 || round || sticky; + int iValue = (int) (significand >>> threshShift); + if ((iValue & 3) != 1 || floatSticky) { + iValue++; + } + floatBits |= (((((int) exponent) + (FloatConsts.EXP_BIAS - 1))) << SINGLE_EXP_SHIFT) + (iValue >> 1); + } + } else { + if (exponent < FloatConsts.MIN_SUB_EXPONENT - 1) { + // 0 + } else { + // exponent == -127 ==> threshShift = 53 - 2 + (-149) - (-127) = 53 - 24 + int threshShift = (int) ((DoubleConsts.SIGNIFICAND_WIDTH - 2 + FloatConsts.MIN_SUB_EXPONENT) - exponent); + assert threshShift >= DoubleConsts.SIGNIFICAND_WIDTH - FloatConsts.SIGNIFICAND_WIDTH; + assert threshShift < DoubleConsts.SIGNIFICAND_WIDTH; + boolean floatSticky = (significand & ((1L << threshShift) - 1)) != 0 || round || sticky; + int iValue = (int) (significand >>> threshShift); + if ((iValue & 3) != 1 || floatSticky) { + iValue++; + } + floatBits |= iValue >> 1; + } + } + float fValue = Float.intBitsToFloat(floatBits); + // Check for overflow and update exponent accordingly. if (exponent > DoubleConsts.MAX_EXPONENT) { // Infinite result // overflow to properly signed infinity @@ -2390,87 +2505,7 @@ public class FloatingDecimal{ Double.longBitsToDouble(significand | DoubleConsts.SIGN_BIT_MASK) : Double.longBitsToDouble(significand ); - int roundDir = 0; - // - // Set roundingDir variable field of fd properly so - // that the input string can be properly rounded to a - // float value. There are two cases to consider: - // - // 1. rounding to double discards sticky bit - // information that would change the result of a float - // rounding (near halfway case between two floats) - // - // 2. rounding to double rounds up when rounding up - // would not occur when rounding to float. - // - // For former case only needs to be considered when - // the bits rounded away when casting to float are all - // zero; otherwise, float round bit is properly set - // and sticky will already be true. - // - // The lower exponent bound for the code below is the - // minimum (normalized) subnormal exponent - 1 since a - // value with that exponent can round up to the - // minimum subnormal value and the sticky bit - // information must be preserved (i.e. case 1). - // - if ((exponent >= FloatConsts.MIN_SUB_EXPONENT - 1) && - (exponent <= FloatConsts.MAX_EXPONENT)) { - // Outside above exponent range, the float value - // will be zero or infinity. - - // - // If the low-order 28 bits of a rounded double - // significand are 0, the double could be a - // half-way case for a rounding to float. If the - // double value is a half-way case, the double - // significand may have to be modified to round - // the the right float value (see the stickyRound - // method). If the rounding to double has lost - // what would be float sticky bit information, the - // double significand must be incremented. If the - // double value's significand was itself - // incremented, the float value may end up too - // large so the increment should be undone. - // - if ((significand & 0xfffffffL) == 0x0L) { - // For negative values, the sign of the - // roundDir is the same as for positive values - // since adding 1 increasing the significand's - // magnitude and subtracting 1 decreases the - // significand's magnitude. If neither round - // nor sticky is true, the double value is - // exact and no adjustment is required for a - // proper float rounding. - if (round || sticky) { - if (leastZero) { // prerounding lsb is 0 - // If round and sticky were both true, - // and the least significant - // significand bit were 0, the rounded - // significand would not have its - // low-order bits be zero. Therefore, - // we only need to adjust the - // significand if round XOR sticky is - // true. - if (round ^ sticky) { - roundDir = 1; - } - } else { // prerounding lsb is 1 - // If the prerounding lsb is 1 and the - // resulting significand has its - // low-order bits zero, the significand - // was incremented. Here, we undo the - // increment, which will ensure the - // right guard and sticky bits for the - // float rounding. - if (round) { - roundDir = -1; - } - } - } - } - } - return new PreparedASCIIToBinaryBuffer(value,roundDir); + return new PreparedASCIIToBinaryBuffer(value, fValue); } } } diff --git a/test/java/lang/Double/ParseDouble.java b/test/java/lang/Double/ParseDouble.java index 504f5bc3d..6cefad6ee 100644 --- a/test/java/lang/Double/ParseDouble.java +++ b/test/java/lang/Double/ParseDouble.java @@ -23,20 +23,106 @@ /* * @test - * @bug 4160406 4705734 4707389 4826774 4895911 4421494 7021568 7039369 + * @bug 4160406 4705734 4707389 4826774 4895911 4421494 6358355 7021568 7039369 4396272 * @summary Test for Double.parseDouble method and acceptance regex */ -import java.util.regex.*; import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.regex.*; public class ParseDouble { + private static final BigDecimal HALF = BigDecimal.valueOf(0.5); + + private static void fail(String val, double n) { + throw new RuntimeException("Double.parseDouble failed. String:" + + val + " Result:" + n); + } + + private static void check(String val) { + double n = Double.parseDouble(val); + boolean isNegativeN = n < 0 || n == 0 && 1/n < 0; + double na = Math.abs(n); + String s = val.trim().toLowerCase(); + switch (s.charAt(s.length() - 1)) { + case 'd': + case 'f': + s = s.substring(0, s.length() - 1); + break; + } + boolean isNegative = false; + if (s.charAt(0) == '+') { + s = s.substring(1); + } else if (s.charAt(0) == '-') { + s = s.substring(1); + isNegative = true; + } + if (s.equals("nan")) { + if (!Double.isNaN(n)) { + fail(val, n); + } + return; + } + if (Double.isNaN(n)) { + fail(val, n); + } + if (isNegativeN != isNegative) + fail(val, n); + if (s.equals("infinity")) { + if (na != Double.POSITIVE_INFINITY) { + fail(val, n); + } + return; + } + BigDecimal bd; + if (s.startsWith("0x")) { + s = s.substring(2); + int indP = s.indexOf('p'); + long exp = Long.parseLong(s.substring(indP + 1)); + int indD = s.indexOf('.'); + String significand; + if (indD >= 0) { + significand = s.substring(0, indD) + s.substring(indD + 1, indP); + exp -= 4*(indP - indD - 1); + } else { + significand = s.substring(0, indP); + } + bd = new BigDecimal(new BigInteger(significand, 16)); + if (exp >= 0) { + bd = bd.multiply(BigDecimal.valueOf(2).pow((int)exp)); + } else { + bd = bd.divide(BigDecimal.valueOf(2).pow((int)-exp)); + } + } else { + bd = new BigDecimal(s); + } + BigDecimal l, u; + if (Double.isInfinite(na)) { + l = new BigDecimal(Double.MAX_VALUE).add(new BigDecimal(Math.ulp(Double.MAX_VALUE)).multiply(HALF)); + u = null; + } else { + l = new BigDecimal(na).subtract(new BigDecimal(Math.ulp(Math.nextUp(-na))).multiply(HALF)); + u = new BigDecimal(na).add(new BigDecimal(Math.ulp(n)).multiply(HALF)); + } + int cmpL = bd.compareTo(l); + int cmpU = u != null ? bd.compareTo(u) : -1; + if ((Double.doubleToLongBits(n) & 1) != 0) { + if (cmpL <= 0 || cmpU >= 0) { + fail(val, n); + } + } else { + if (cmpL < 0 || cmpU > 0) { + fail(val, n); + } + } + } + private static void check(String val, double expected) { double n = Double.parseDouble(val); if (n != expected) - throw new RuntimeException("Double.parseDouble failed. String:" + - val + " Result:" + n); + fail(val, n); + check(val); } private static void rudimentaryTest() { @@ -460,6 +546,7 @@ public class ParseDouble { try { d = Double.parseDouble(input[i]); + check(input[i]); } catch (NumberFormatException e) { if (! exceptionalInput) { @@ -560,12 +647,13 @@ public class ParseDouble { * 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); /* @@ -578,17 +666,69 @@ public class ParseDouble { 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"); } + /** + * For each power of two, test at boundaries of + * region that should convert to that value. + */ + private static void testPowers() { + for(int i = -1074; i <= +1023; i++) { + double d = Math.scalb(1.0, i); + BigDecimal d_BD = new BigDecimal(d); + + BigDecimal lowerBound = d_BD.subtract(new BigDecimal(Math.ulp(Math.nextUp(-d))).multiply(HALF)); + BigDecimal upperBound = d_BD.add(new BigDecimal(Math.ulp(d)).multiply(HALF)); + + check(lowerBound.toString()); + check(upperBound.toString()); + } + check(new BigDecimal(Double.MAX_VALUE).add(new BigDecimal(Math.ulp(Double.MAX_VALUE)).multiply(HALF)).toString()); + } 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++) { @@ -620,6 +760,7 @@ public class ParseDouble { testRegex(paddedBadStrings, true); testSubnormalPowers(); + testPowers(); testStrictness(); } } diff --git a/test/java/lang/Float/ParseFloat.java b/test/java/lang/Float/ParseFloat.java index 4d646668f..f78cef77d 100644 --- a/test/java/lang/Float/ParseFloat.java +++ b/test/java/lang/Float/ParseFloat.java @@ -23,17 +23,105 @@ /* * @test - * @bug 4160406 4705734 4707389 + * @bug 4160406 4705734 4707389 6358355 7032154 * @summary Tests for Float.parseFloat method */ +import java.math.BigDecimal; +import java.math.BigInteger; + public class ParseFloat { + private static final BigDecimal HALF = BigDecimal.valueOf(0.5); + + private static void fail(String val, float n) { + throw new RuntimeException("Float.parseFloat failed. String:" + + val + " Result:" + n); + } + + private static void check(String val) { + float n = Float.parseFloat(val); + boolean isNegativeN = n < 0 || n == 0 && 1/n < 0; + float na = Math.abs(n); + String s = val.trim().toLowerCase(); + switch (s.charAt(s.length() - 1)) { + case 'd': + case 'f': + s = s.substring(0, s.length() - 1); + break; + } + boolean isNegative = false; + if (s.charAt(0) == '+') { + s = s.substring(1); + } else if (s.charAt(0) == '-') { + s = s.substring(1); + isNegative = true; + } + if (s.equals("nan")) { + if (!Float.isNaN(n)) { + fail(val, n); + } + return; + } + if (Float.isNaN(n)) { + fail(val, n); + } + if (isNegativeN != isNegative) + fail(val, n); + if (s.equals("infinity")) { + if (na != Float.POSITIVE_INFINITY) { + fail(val, n); + } + return; + } + BigDecimal bd; + if (s.startsWith("0x")) { + s = s.substring(2); + int indP = s.indexOf('p'); + long exp = Long.parseLong(s.substring(indP + 1)); + int indD = s.indexOf('.'); + String significand; + if (indD >= 0) { + significand = s.substring(0, indD) + s.substring(indD + 1, indP); + exp -= 4*(indP - indD - 1); + } else { + significand = s.substring(0, indP); + } + bd = new BigDecimal(new BigInteger(significand, 16)); + if (exp >= 0) { + bd = bd.multiply(BigDecimal.valueOf(2).pow((int)exp)); + } else { + bd = bd.divide(BigDecimal.valueOf(2).pow((int)-exp)); + } + } else { + bd = new BigDecimal(s); + } + BigDecimal l, u; + if (Float.isInfinite(na)) { + l = new BigDecimal(Float.MAX_VALUE).add(new BigDecimal(Math.ulp(Float.MAX_VALUE)).multiply(HALF)); + u = null; + } else { + l = new BigDecimal(na).subtract(new BigDecimal(Math.ulp(-Math.nextUp(-na))).multiply(HALF)); + u = new BigDecimal(na).add(new BigDecimal(Math.ulp(n)).multiply(HALF)); + } + int cmpL = bd.compareTo(l); + int cmpU = u != null ? bd.compareTo(u) : -1; + if ((Float.floatToIntBits(n) & 1) != 0) { + if (cmpL <= 0 || cmpU >= 0) { + fail(val, n); + } + } else { + if (cmpL < 0 || cmpU > 0) { + fail(val, n); + } + } + } + private static void check(String val, float expected) { float n = Float.parseFloat(val); if (n != expected) - throw new RuntimeException("Float.parseFloat failed. String:" + - val + " Result:" + n); + fail(val, n); + check(val); } private static void rudimentaryTest() { @@ -47,6 +135,17 @@ public class ParseFloat { check("-10", (float) -10.0); check("-10.00", (float) -10.0); check("-10.01", (float) -10.01); + + // bug 6358355 + check("144115196665790480", 0x1.000002p57f); + check("144115196665790481", 0x1.000002p57f); + check("0.050000002607703203", 0.05f); + check("0.050000002607703204", 0.05f); + check("0.050000002607703205", 0.05f); + check("0.050000002607703206", 0.05f); + check("0.050000002607703207", 0.05f); + check("0.050000002607703208", 0.05f); + check("0.050000002607703209", 0.050000004f); } static String badStrings[] = { @@ -182,6 +281,7 @@ public class ParseFloat { try { d = Float.parseFloat(input[i]); + check(input[i]); } catch (NumberFormatException e) { if (! exceptionalInput) { @@ -199,6 +299,24 @@ public class ParseFloat { } } + /** + * For each power of two, test at boundaries of + * region that should convert to that value. + */ + private static void testPowers() { + for(int i = -149; i <= +127; i++) { + float f = Math.scalb(1.0f, i); + BigDecimal f_BD = new BigDecimal(f); + + BigDecimal lowerBound = f_BD.subtract(new BigDecimal(Math.ulp(-Math.nextUp(-f))).multiply(HALF)); + BigDecimal upperBound = f_BD.add(new BigDecimal(Math.ulp(f)).multiply(HALF)); + + check(lowerBound.toString()); + check(upperBound.toString()); + } + check(new BigDecimal(Float.MAX_VALUE).add(new BigDecimal(Math.ulp(Float.MAX_VALUE)).multiply(HALF)).toString()); + } + public static void main(String[] args) throws Exception { rudimentaryTest(); @@ -206,5 +324,7 @@ public class ParseFloat { testParsing(paddedGoodStrings, false); testParsing(badStrings, true); testParsing(paddedBadStrings, true); + + testPowers(); } } diff --git a/test/sun/misc/FloatingDecimal/TestFDBigInteger.java b/test/sun/misc/FloatingDecimal/TestFDBigInteger.java index 5818e9402..cfdb7b849 100644 --- a/test/sun/misc/FloatingDecimal/TestFDBigInteger.java +++ b/test/sun/misc/FloatingDecimal/TestFDBigInteger.java @@ -351,6 +351,10 @@ public class TestFDBigInteger { if (!isImmutable && diff != left) { throw new Exception("leftInplaceSub of doesn't reuse its argument"); } + if (isImmutable) { + check(biLeft, left, "leftInplaceSub corrupts its left immutable argument"); + } + check(biRight, right, "leftInplaceSub corrupts its right argument"); check(biLeft.subtract(biRight), diff, "leftInplaceSub returns wrong result"); } @@ -381,6 +385,10 @@ public class TestFDBigInteger { if (!isImmutable && diff != right) { throw new Exception("rightInplaceSub of doesn't reuse its argument"); } + check(biLeft, left, "leftInplaceSub corrupts its left argument"); + if (isImmutable) { + check(biRight, right, "leftInplaceSub corrupts its right immutable argument"); + } try { check(biLeft.subtract(biRight), diff, "rightInplaceSub returns wrong result"); } catch (Exception e) { -- GitLab