mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-19 23:48:50 +00:00
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 Correct rounding and monotonicity problems in floats and doubles Co-authored-by: Louis Wasserman <lowasser@google.com> Reviewed-by: bpb, martin
This commit is contained in:
parent
af996219c7
commit
f52ad64ccc
@ -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.
|
||||
//
|
||||
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;
|
||||
} else 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;
|
||||
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));
|
||||
|
||||
//
|
||||
// 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.
|
||||
// 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
|
||||
//
|
||||
// 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 (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.
|
||||
//
|
||||
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;
|
||||
}
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
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.
|
||||
}
|
||||
|
||||
}
|
||||
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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user