提交 c0127d85 编写于 作者: B bpb

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: NDmitry Nadezhin &lt;dmitry.nadezhin@oracle.com&gt;, Louis Wasserman <lowasser@google.com>
上级 1c14b0a7
......@@ -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;
......
......@@ -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<<SINGLE_EXP_SHIFT;
static final int SINGLE_MAX_DECIMAL_DIGITS = 7;
static final int SINGLE_MAX_DECIMAL_EXPONENT = 38;
static final int SINGLE_MIN_DECIMAL_EXPONENT = -45;
static final int SINGLE_MAX_NDIGITS = 200;
static final int INT_DECIMAL_DIGITS = 9;
......@@ -1002,15 +1004,11 @@ public class FloatingDecimal{
*/
static class PreparedASCIIToBinaryBuffer implements ASCIIToBinaryConverter {
final private double doubleVal;
private int roundDir = 0;
final private float floatVal;
public PreparedASCIIToBinaryBuffer(double doubleVal) {
public PreparedASCIIToBinaryBuffer(double doubleVal, float floatVal) {
this.doubleVal = doubleVal;
}
public PreparedASCIIToBinaryBuffer(double doubleVal, int roundDir) {
this.doubleVal = doubleVal;
this.roundDir = roundDir;
this.floatVal = floatVal;
}
@Override
......@@ -1020,15 +1018,15 @@ public class FloatingDecimal{
@Override
public float floatValue() {
return stickyRound(doubleVal,roundDir);
return floatVal;
}
}
static final ASCIIToBinaryConverter A2BC_POSITIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.POSITIVE_INFINITY);
static final ASCIIToBinaryConverter A2BC_NEGATIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.NEGATIVE_INFINITY);
static final ASCIIToBinaryConverter A2BC_NOT_A_NUMBER = new PreparedASCIIToBinaryBuffer(Double.NaN);
static final ASCIIToBinaryConverter A2BC_POSITIVE_ZERO = new PreparedASCIIToBinaryBuffer(0.0d);
static final ASCIIToBinaryConverter A2BC_NEGATIVE_ZERO = new PreparedASCIIToBinaryBuffer(-0.0d);
static final ASCIIToBinaryConverter A2BC_POSITIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
static final ASCIIToBinaryConverter A2BC_NEGATIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
static final ASCIIToBinaryConverter A2BC_NOT_A_NUMBER = new PreparedASCIIToBinaryBuffer(Double.NaN, Float.NaN);
static final ASCIIToBinaryConverter A2BC_POSITIVE_ZERO = new PreparedASCIIToBinaryBuffer(0.0d, 0.0f);
static final ASCIIToBinaryConverter A2BC_NEGATIVE_ZERO = new PreparedASCIIToBinaryBuffer(-0.0d, -0.0f);
/**
* A buffered implementation of <code>ASCIIToBinaryConverter</code>.
......@@ -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);
}
}
}
......
......@@ -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();
}
}
......@@ -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();
}
}
......@@ -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) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册