8010803: Number to String conversion functionality overhaul

Reviewed-by: attila, lagergren
This commit is contained in:
Hannes Wallnöfer 2015-11-11 15:22:14 +01:00
parent 534ff3deff
commit 15ef19ee62
26 changed files with 307175 additions and 799 deletions

@ -311,6 +311,10 @@
<fileset dir="${test.src.dir}/jdk/nashorn/internal/runtime/test/resources"/>
</copy>
<copy todir="${build.test.classes.dir}/jdk/nashorn/internal/runtime/doubleconv/test/resources">
<fileset dir="${test.src.dir}/jdk/nashorn/internal/runtime/doubleconv/test/resources"/>
</copy>
<copy todir="${build.test.classes.dir}/jdk/nashorn/api/scripting/test/resources">
<fileset dir="${test.src.dir}/jdk/nashorn/api/scripting/test/resources"/>
</copy>
@ -436,6 +440,7 @@ grant codeBase "file:/${basedir}/test/script/markdown.js" {
<include name="**/runtime/test/*Test.class"/>
<include name="**/runtime/regexp/test/*Test.class"/>
<include name="**/runtime/regexp/joni/test/*Test.class"/>
<include name="**/runtime/doubleconv/test/*Test.class"/>
<include name="**/framework/*Test.class"/>
</fileset>
</target>

@ -49,6 +49,7 @@ import jdk.nashorn.internal.runtime.JSType;
import jdk.nashorn.internal.runtime.PropertyMap;
import jdk.nashorn.internal.runtime.ScriptObject;
import jdk.nashorn.internal.runtime.ScriptRuntime;
import jdk.nashorn.internal.runtime.doubleconv.DoubleConversion;
import jdk.nashorn.internal.runtime.linker.PrimitiveLookup;
/**
@ -184,13 +185,7 @@ public final class NativeNumber extends ScriptObject {
return JSType.toString(x);
}
final NumberFormat format = NumberFormat.getNumberInstance(Locale.US);
format.setMinimumFractionDigits(fractionDigits);
format.setMaximumFractionDigits(fractionDigits);
format.setGroupingUsed(false);
format.setRoundingMode(RoundingMode.HALF_UP);
return format.format(x);
return DoubleConversion.toFixed(x, fractionDigits);
}
/**
@ -267,7 +262,7 @@ public final class NativeNumber extends ScriptObject {
return "0";
}
return fixExponent(String.format(Locale.US, "%." + p + "g", x), false);
return DoubleConversion.toPrecision(x, p);
}
/**

@ -42,6 +42,7 @@ import jdk.nashorn.internal.codegen.types.Type;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.parser.Lexer;
import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
import jdk.nashorn.internal.runtime.doubleconv.DoubleConversion;
import jdk.nashorn.internal.runtime.linker.Bootstrap;
/**
@ -687,7 +688,7 @@ public enum JSType {
return "NaN";
}
return NumberToString.stringFor(num);
return DoubleConversion.toShortestString(num);
}
/**

@ -1,786 +0,0 @@
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.runtime;
import java.math.BigInteger;
/**
* JavaScript number to string conversion, refinement of sun.misc.FloatingDecimal.
*/
public final class NumberToString {
/** Is not a number flag */
private final boolean isNaN;
/** Is a negative number flag. */
private boolean isNegative;
/** Decimal exponent value (for E notation.) */
private int decimalExponent;
/** Actual digits. */
private char digits[];
/** Number of digits to use. (nDigits <= digits.length). */
private int nDigits;
/*
* IEEE-754 constants.
*/
//private static final long signMask = 0x8000000000000000L;
private static final int expMask = 0x7FF;
private static final long fractMask = 0x000F_FFFF_FFFF_FFFFL;
private static final int expShift = 52;
private static final int expBias = 1_023;
private static final long fractHOB = (1L << expShift);
private static final long expOne = ((long)expBias) << expShift;
private static final int maxSmallBinExp = 62;
private static final int minSmallBinExp = -(63 / 3);
/** Powers of 5 fitting a long. */
private static final long powersOf5[] = {
1L,
5L,
5L * 5,
5L * 5 * 5,
5L * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5,
5L * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
};
// Approximately ceil(log2(longPowers5[i])).
private static final int nBitsPowerOf5[] = {
0,
3,
5,
7,
10,
12,
14,
17,
19,
21,
24,
26,
28,
31,
33,
35,
38,
40,
42,
45,
47,
49,
52,
54,
56,
59,
61
};
/** Digits used for infinity result. */
private static final char infinityDigits[] = { 'I', 'n', 'f', 'i', 'n', 'i', 't', 'y' };
/** Digits used for NaN result. */
private static final char nanDigits[] = { 'N', 'a', 'N' };
/** Zeros used to pad result. */
private static final char zeroes[] = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0' };
/**
* Convert a number into a JavaScript string.
* @param value Double to convert.
* @return JavaScript formated number.
*/
public static String stringFor(final double value) {
return new NumberToString(value).toString();
}
/*
* Constructor.
*/
private NumberToString(final double value) {
// Double as bits.
long bits = Double.doubleToLongBits(value);
// Get upper word.
final int upper = (int)(bits >> 32);
// Detect sign.
isNegative = upper < 0;
// Extract exponent.
int exponent = (upper >> (expShift - 32)) & expMask;
// Clear sign and exponent.
bits &= fractMask;
// Detect NaN.
if (exponent == expMask) {
isNaN = true;
// Detect Infinity.
if (bits == 0L) {
digits = infinityDigits;
} else {
digits = nanDigits;
isNegative = false;
}
nDigits = digits.length;
return;
}
// We have a working double.
isNaN = false;
int nSignificantBits;
// Detect denormalized value.
if (exponent == 0) {
// Detect zero value.
if (bits == 0L) {
decimalExponent = 0;
digits = zeroes;
nDigits = 1;
return;
}
// Normalize value, using highest significant bit as HOB.
while ((bits & fractHOB) == 0L) {
bits <<= 1;
exponent -= 1;
}
// Compute number of significant bits.
nSignificantBits = expShift + exponent +1;
// Bias exponent by HOB.
exponent += 1;
} else {
// Add implicit HOB.
bits |= fractHOB;
// Compute number of significant bits.
nSignificantBits = expShift + 1;
}
// Unbias exponent (represents bit shift).
exponent -= expBias;
// Determine the number of significant bits in the fraction.
final int nFractBits = countSignificantBits(bits);
// Number of bits to the right of the decimal.
final int nTinyBits = Math.max(0, nFractBits - exponent - 1);
// Computed decimal exponent.
int decExponent;
if (exponent <= maxSmallBinExp && exponent >= minSmallBinExp) {
// Look more closely at the number to decide if,
// with scaling by 10^nTinyBits, the result will fit in
// a long.
if (nTinyBits < powersOf5.length && (nFractBits + nBitsPowerOf5[nTinyBits]) < 64) {
/*
* We can do this:
* take the fraction bits, which are normalized.
* (a) nTinyBits == 0: Shift left or right appropriately
* to align the binary point at the extreme right, i.e.
* where a long int point is expected to be. The integer
* result is easily converted to a string.
* (b) nTinyBits > 0: Shift right by expShift - nFractBits,
* which effectively converts to long and scales by
* 2^nTinyBits. Then multiply by 5^nTinyBits to
* complete the scaling. We know this won't overflow
* because we just counted the number of bits necessary
* in the result. The integer you get from this can
* then be converted to a string pretty easily.
*/
if (nTinyBits == 0) {
long halfULP;
if (exponent > nSignificantBits) {
halfULP = 1L << (exponent - nSignificantBits - 1);
} else {
halfULP = 0L;
}
if (exponent >= expShift) {
bits <<= exponent - expShift;
} else {
bits >>>= expShift - exponent;
}
// Discard non-significant low-order bits, while rounding,
// up to insignificant value.
int i;
for (i = 0; halfULP >= 10L; i++) {
halfULP /= 10L;
}
/**
* This is the easy subcase --
* all the significant bits, after scaling, are held in bits.
* isNegative and decExponent tell us what processing and scaling
* has already been done. Exceptional cases have already been
* stripped out.
* In particular:
* bits is a finite number (not Infinite, nor NaN)
* bits > 0L (not zero, nor negative).
*
* The only reason that we develop the digits here, rather than
* calling on Long.toString() is that we can do it a little faster,
* and besides want to treat trailing 0s specially. If Long.toString
* changes, we should re-evaluate this strategy!
*/
int decExp = 0;
if (i != 0) {
// 10^i == 5^i * 2^i
final long powerOf10 = powersOf5[i] << i;
final long residue = bits % powerOf10;
bits /= powerOf10;
decExp += i;
if (residue >= (powerOf10 >> 1)) {
// Round up based on the low-order bits we're discarding.
bits++;
}
}
int ndigits = 20;
final char[] digits0 = new char[26];
int digitno = ndigits - 1;
int c = (int)(bits % 10L);
bits /= 10L;
while (c == 0) {
decExp++;
c = (int)(bits % 10L);
bits /= 10L;
}
while (bits != 0L) {
digits0[digitno--] = (char)(c + '0');
decExp++;
c = (int)(bits % 10L);
bits /= 10;
}
digits0[digitno] = (char)(c + '0');
ndigits -= digitno;
final char[] result = new char[ndigits];
System.arraycopy(digits0, digitno, result, 0, ndigits);
this.digits = result;
this.decimalExponent = decExp + 1;
this.nDigits = ndigits;
return;
}
}
}
/*
* This is the hard case. We are going to compute large positive
* integers B and S and integer decExp, s.t.
* d = (B / S) * 10^decExp
* 1 <= B / S < 10
* Obvious choices are:
* decExp = floor(log10(d))
* B = d * 2^nTinyBits * 10^max(0, -decExp)
* S = 10^max(0, decExp) * 2^nTinyBits
* (noting that nTinyBits has already been forced to non-negative)
* I am also going to compute a large positive integer
* M = (1/2^nSignificantBits) * 2^nTinyBits * 10^max(0, -decExp)
* i.e. M is (1/2) of the ULP of d, scaled like B.
* When we iterate through dividing B/S and picking off the
* quotient bits, we will know when to stop when the remainder
* is <= M.
*
* We keep track of powers of 2 and powers of 5.
*/
/*
* Estimate decimal exponent. (If it is small-ish,
* we could double-check.)
*
* First, scale the mantissa bits such that 1 <= d2 < 2.
* We are then going to estimate
* log10(d2) ~=~ (d2-1.5)/1.5 + log(1.5)
* and so we can estimate
* log10(d) ~=~ log10(d2) + binExp * log10(2)
* take the floor and call it decExp.
*/
final double d2 = Double.longBitsToDouble(expOne | (bits & ~fractHOB));
decExponent = (int)Math.floor((d2 - 1.5D) * 0.289529654D + 0.176091259D + exponent * 0.301029995663981D);
// Powers of 2 and powers of 5, respectively, in B.
final int B5 = Math.max(0, -decExponent);
int B2 = B5 + nTinyBits + exponent;
// Powers of 2 and powers of 5, respectively, in S.
final int S5 = Math.max(0, decExponent);
int S2 = S5 + nTinyBits;
// Powers of 2 and powers of 5, respectively, in M.
final int M5 = B5;
int M2 = B2 - nSignificantBits;
/*
* The long integer fractBits contains the (nFractBits) interesting
* bits from the mantissa of d (hidden 1 added if necessary) followed
* by (expShift + 1 - nFractBits) zeros. In the interest of compactness,
* I will shift out those zeros before turning fractBits into a
* BigInteger. The resulting whole number will be
* d * 2^(nFractBits - 1 - binExp).
*/
bits >>>= expShift + 1 - nFractBits;
B2 -= nFractBits - 1;
final int common2factor = Math.min(B2, S2);
B2 -= common2factor;
S2 -= common2factor;
M2 -= common2factor;
/*
* HACK!!For exact powers of two, the next smallest number
* is only half as far away as we think (because the meaning of
* ULP changes at power-of-two bounds) for this reason, we
* hack M2. Hope this works.
*/
if (nFractBits == 1) {
M2 -= 1;
}
if (M2 < 0) {
// Oops. Since we cannot scale M down far enough,
// we must scale the other values up.
B2 -= M2;
S2 -= M2;
M2 = 0;
}
/*
* Construct, Scale, iterate.
* Some day, we'll write a stopping test that takes
* account of the asymmetry of the spacing of floating-point
* numbers below perfect powers of 2
* 26 Sept 96 is not that day.
* So we use a symmetric test.
*/
final char digits0[] = this.digits = new char[32];
int ndigit;
boolean low, high;
long lowDigitDifference;
int q;
/*
* Detect the special cases where all the numbers we are about
* to compute will fit in int or long integers.
* In these cases, we will avoid doing BigInteger arithmetic.
* We use the same algorithms, except that we "normalize"
* our FDBigInts before iterating. This is to make division easier,
* as it makes our fist guess (quotient of high-order words)
* more accurate!
*/
// Binary digits needed to represent B, approx.
final int Bbits = nFractBits + B2 + ((B5 < nBitsPowerOf5.length) ? nBitsPowerOf5[B5] : (B5*3));
// Binary digits needed to represent 10*S, approx.
final int tenSbits = S2 + 1 + (((S5 + 1) < nBitsPowerOf5.length) ? nBitsPowerOf5[(S5 + 1)] : ((S5 + 1) * 3));
if (Bbits < 64 && tenSbits < 64) {
long b = (bits * powersOf5[B5]) << B2;
final long s = powersOf5[S5] << S2;
long m = powersOf5[M5] << M2;
final long tens = s * 10L;
/*
* Unroll the first iteration. If our decExp estimate
* was too high, our first quotient will be zero. In this
* case, we discard it and decrement decExp.
*/
ndigit = 0;
q = (int)(b / s);
b = 10L * (b % s);
m *= 10L;
low = b < m;
high = (b + m) > tens;
if (q == 0 && !high) {
// Ignore leading zero.
decExponent--;
} else {
digits0[ndigit++] = (char)('0' + q);
}
if (decExponent < -3 || decExponent >= 8) {
high = low = false;
}
while (!low && !high) {
q = (int)(b / s);
b = 10 * (b % s);
m *= 10;
if (m > 0L) {
low = b < m;
high = (b + m) > tens;
} else {
low = true;
high = true;
}
if (low && q == 0) {
break;
}
digits0[ndigit++] = (char)('0' + q);
}
lowDigitDifference = (b << 1) - tens;
} else {
/*
* We must do BigInteger arithmetic.
* First, construct our BigInteger initial values.
*/
BigInteger Bval = multiplyPowerOf5And2(BigInteger.valueOf(bits), B5, B2);
BigInteger Sval = constructPowerOf5And2(S5, S2);
BigInteger Mval = constructPowerOf5And2(M5, M2);
// Normalize so that BigInteger division works better.
final int shiftBias = Long.numberOfLeadingZeros(bits) - 4;
Bval = Bval.shiftLeft(shiftBias);
Mval = Mval.shiftLeft(shiftBias);
Sval = Sval.shiftLeft(shiftBias);
final BigInteger tenSval = Sval.multiply(BigInteger.TEN);
/*
* Unroll the first iteration. If our decExp estimate
* was too high, our first quotient will be zero. In this
* case, we discard it and decrement decExp.
*/
ndigit = 0;
BigInteger[] quoRem = Bval.divideAndRemainder(Sval);
q = quoRem[0].intValue();
Bval = quoRem[1].multiply(BigInteger.TEN);
Mval = Mval.multiply(BigInteger.TEN);
low = (Bval.compareTo(Mval) < 0);
high = (Bval.add(Mval).compareTo(tenSval) > 0);
if (q == 0 && !high) {
// Ignore leading zero.
decExponent--;
} else {
digits0[ndigit++] = (char)('0' + q);
}
if (decExponent < -3 || decExponent >= 8) {
high = low = false;
}
while(!low && !high) {
quoRem = Bval.divideAndRemainder(Sval);
q = quoRem[0].intValue();
Bval = quoRem[1].multiply(BigInteger.TEN);
Mval = Mval.multiply(BigInteger.TEN);
low = (Bval.compareTo(Mval) < 0);
high = (Bval.add(Mval).compareTo(tenSval) > 0);
if (low && q == 0) {
break;
}
digits0[ndigit++] = (char)('0' + q);
}
if (high && low) {
Bval = Bval.shiftLeft(1);
lowDigitDifference = Bval.compareTo(tenSval);
} else {
lowDigitDifference = 0L;
}
}
this.decimalExponent = decExponent + 1;
this.digits = digits0;
this.nDigits = ndigit;
/*
* Last digit gets rounded based on stopping condition.
*/
if (high) {
if (low) {
if (lowDigitDifference == 0L) {
// it's a tie!
// choose based on which digits we like.
if ((digits0[nDigits - 1] & 1) != 0) {
roundup();
}
} else if (lowDigitDifference > 0) {
roundup();
}
} else {
roundup();
}
}
}
/**
* Count number of significant bits.
* @param bits Double's fraction.
* @return Number of significant bits.
*/
private static int countSignificantBits(final long bits) {
if (bits != 0) {
return 64 - Long.numberOfLeadingZeros(bits) - Long.numberOfTrailingZeros(bits);
}
return 0;
}
/*
* Cache big powers of 5 handy for future reference.
*/
private static BigInteger powerOf5Cache[];
/**
* Determine the largest power of 5 needed (as BigInteger.)
* @param power Power of 5.
* @return BigInteger of power of 5.
*/
private static BigInteger bigPowerOf5(final int power) {
if (powerOf5Cache == null) {
powerOf5Cache = new BigInteger[power + 1];
} else if (powerOf5Cache.length <= power) {
final BigInteger t[] = new BigInteger[ power+1 ];
System.arraycopy(powerOf5Cache, 0, t, 0, powerOf5Cache.length);
powerOf5Cache = t;
}
if (powerOf5Cache[power] != null) {
return powerOf5Cache[power];
} else if (power < powersOf5.length) {
return powerOf5Cache[power] = BigInteger.valueOf(powersOf5[power]);
} else {
// Construct the value recursively.
// in order to compute 5^p,
// compute its square root, 5^(p/2) and square.
// or, let q = p / 2, r = p -q, then
// 5^p = 5^(q+r) = 5^q * 5^r
final int q = power >> 1;
final int r = power - q;
BigInteger bigQ = powerOf5Cache[q];
if (bigQ == null) {
bigQ = bigPowerOf5(q);
}
if (r < powersOf5.length) {
return (powerOf5Cache[power] = bigQ.multiply(BigInteger.valueOf(powersOf5[r])));
}
BigInteger bigR = powerOf5Cache[ r ];
if (bigR == null) {
bigR = bigPowerOf5(r);
}
return (powerOf5Cache[power] = bigQ.multiply(bigR));
}
}
/**
* Multiply BigInteger by powers of 5 and 2 (i.e., 10)
* @param value Value to multiply.
* @param p5 Power of 5.
* @param p2 Power of 2.
* @return Result.
*/
private static BigInteger multiplyPowerOf5And2(final BigInteger value, final int p5, final int p2) {
BigInteger returnValue = value;
if (p5 != 0) {
returnValue = returnValue.multiply(bigPowerOf5(p5));
}
if (p2 != 0) {
returnValue = returnValue.shiftLeft(p2);
}
return returnValue;
}
/**
* Construct a BigInteger power of 5 and 2 (i.e., 10)
* @param p5 Power of 5.
* @param p2 Power of 2.
* @return Result.
*/
private static BigInteger constructPowerOf5And2(final int p5, final int p2) {
BigInteger v = bigPowerOf5(p5);
if (p2 != 0) {
v = v.shiftLeft(p2);
}
return v;
}
/**
* Round up last digit by adding one to the least significant digit.
* In the unlikely event there is a carry out, deal with it.
* assert that this will only happen where there
* is only one digit, e.g. (float)1e-44 seems to do it.
*/
private void roundup() {
int i;
int q = digits[ i = (nDigits-1)];
while (q == '9' && i > 0) {
if (decimalExponent < 0) {
nDigits--;
} else {
digits[i] = '0';
}
q = digits[--i];
}
if (q == '9') {
// Carryout! High-order 1, rest 0s, larger exp.
decimalExponent += 1;
digits[0] = '1';
return;
}
digits[i] = (char)(q + 1);
}
/**
* Format final number string.
* @return Formatted string.
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(32);
if (isNegative) {
sb.append('-');
}
if (isNaN) {
sb.append(digits, 0, nDigits);
} else {
if (decimalExponent > 0 && decimalExponent <= 21) {
final int charLength = Math.min(nDigits, decimalExponent);
sb.append(digits, 0, charLength);
if (charLength < decimalExponent) {
sb.append(zeroes, 0, decimalExponent - charLength);
} else if (charLength < nDigits) {
sb.append('.');
sb.append(digits, charLength, nDigits - charLength);
}
} else if (decimalExponent <=0 && decimalExponent > -6) {
sb.append('0');
sb.append('.');
if (decimalExponent != 0) {
sb.append(zeroes, 0, -decimalExponent);
}
sb.append(digits, 0, nDigits);
} else {
sb.append(digits[0]);
if (nDigits > 1) {
sb.append('.');
sb.append(digits, 1, nDigits - 1);
}
sb.append('e');
final int exponent;
int e;
if (decimalExponent <= 0) {
sb.append('-');
exponent = e = -decimalExponent + 1;
} else {
sb.append('+');
exponent = e = decimalExponent - 1;
}
if (exponent > 99) {
sb.append((char)(e / 100 + '0'));
e %= 100;
}
if (exponent > 9) {
sb.append((char)(e / 10 + '0'));
e %= 10;
}
sb.append((char)(e + '0'));
}
}
return sb.toString();
}
}

@ -0,0 +1,842 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jdk.nashorn.internal.runtime.doubleconv;
import java.util.Arrays;
class Bignum {
// 3584 = 128 * 28. We can represent 2^3584 > 10^1000 accurately.
// This bignum can encode much bigger numbers, since it contains an
// exponent.
static final int kMaxSignificantBits = 3584;
static final int kChunkSize = 32; // size of int
static final int kDoubleChunkSize = 64; // size of long
// With bigit size of 28 we loose some bits, but a double still fits easily
// into two ints, and more importantly we can use the Comba multiplication.
static final int kBigitSize = 28;
static final int kBigitMask = (1 << kBigitSize) - 1;
// Every instance allocates kbigitLength ints on the stack. Bignums cannot
// grow. There are no checks if the stack-allocated space is sufficient.
static final int kBigitCapacity = kMaxSignificantBits / kBigitSize;
private int[] bigits_ = new int[kBigitCapacity];
// A vector backed by bigits_buffer_. This way accesses to the array are
// checked for out-of-bounds errors.
// Vector<int> bigits_;
private int used_digits_;
// The Bignum's value equals value(bigits_) * 2^(exponent_ * kBigitSize).
private int exponent_;
Bignum() {}
void times10() { multiplyByUInt32(10); }
static boolean equal(final Bignum a, final Bignum b) {
return compare(a, b) == 0;
}
static boolean lessEqual(final Bignum a, final Bignum b) {
return compare(a, b) <= 0;
}
static boolean less(final Bignum a, final Bignum b) {
return compare(a, b) < 0;
}
// Returns a + b == c
static boolean plusEqual(final Bignum a, final Bignum b, final Bignum c) {
return plusCompare(a, b, c) == 0;
}
// Returns a + b <= c
static boolean plusLessEqual(final Bignum a, final Bignum b, final Bignum c) {
return plusCompare(a, b, c) <= 0;
}
// Returns a + b < c
static boolean plusLess(final Bignum a, final Bignum b, final Bignum c) {
return plusCompare(a, b, c) < 0;
}
private void ensureCapacity(final int size) {
if (size > kBigitCapacity) {
throw new RuntimeException();
}
}
// BigitLength includes the "hidden" digits encoded in the exponent.
int bigitLength() { return used_digits_ + exponent_; }
// Guaranteed to lie in one Bigit.
void assignUInt16(final char value) {
assert (kBigitSize >= 16);
zero();
if (value == 0) return;
ensureCapacity(1);
bigits_[0] = value;
used_digits_ = 1;
}
void assignUInt64(long value) {
final int kUInt64Size = 64;
zero();
if (value == 0) return;
final int needed_bigits = kUInt64Size / kBigitSize + 1;
ensureCapacity(needed_bigits);
for (int i = 0; i < needed_bigits; ++i) {
bigits_[i] = (int) (value & kBigitMask);
value = value >>> kBigitSize;
}
used_digits_ = needed_bigits;
clamp();
}
void assignBignum(final Bignum other) {
exponent_ = other.exponent_;
for (int i = 0; i < other.used_digits_; ++i) {
bigits_[i] = other.bigits_[i];
}
// Clear the excess digits (if there were any).
for (int i = other.used_digits_; i < used_digits_; ++i) {
bigits_[i] = 0;
}
used_digits_ = other.used_digits_;
}
static long readUInt64(final String str,
final int from,
final int digits_to_read) {
long result = 0;
for (int i = from; i < from + digits_to_read; ++i) {
final int digit = str.charAt(i) - '0';
assert (0 <= digit && digit <= 9);
result = result * 10 + digit;
}
return result;
}
void assignDecimalString(final String str) {
// 2^64 = 18446744073709551616 > 10^19
final int kMaxUint64DecimalDigits = 19;
zero();
int length = str.length();
int pos = 0;
// Let's just say that each digit needs 4 bits.
while (length >= kMaxUint64DecimalDigits) {
final long digits = readUInt64(str, pos, kMaxUint64DecimalDigits);
pos += kMaxUint64DecimalDigits;
length -= kMaxUint64DecimalDigits;
multiplyByPowerOfTen(kMaxUint64DecimalDigits);
addUInt64(digits);
}
final long digits = readUInt64(str, pos, length);
multiplyByPowerOfTen(length);
addUInt64(digits);
clamp();
}
static int hexCharValue(final char c) {
if ('0' <= c && c <= '9') return c - '0';
if ('a' <= c && c <= 'f') return 10 + c - 'a';
assert ('A' <= c && c <= 'F');
return 10 + c - 'A';
}
void assignHexString(final String str) {
zero();
final int length = str.length();
final int needed_bigits = length * 4 / kBigitSize + 1;
ensureCapacity(needed_bigits);
int string_index = length - 1;
for (int i = 0; i < needed_bigits - 1; ++i) {
// These bigits are guaranteed to be "full".
int current_bigit = 0;
for (int j = 0; j < kBigitSize / 4; j++) {
current_bigit += hexCharValue(str.charAt(string_index--)) << (j * 4);
}
bigits_[i] = current_bigit;
}
used_digits_ = needed_bigits - 1;
int most_significant_bigit = 0; // Could be = 0;
for (int j = 0; j <= string_index; ++j) {
most_significant_bigit <<= 4;
most_significant_bigit += hexCharValue(str.charAt(j));
}
if (most_significant_bigit != 0) {
bigits_[used_digits_] = most_significant_bigit;
used_digits_++;
}
clamp();
}
void addUInt64(final long operand) {
if (operand == 0) return;
final Bignum other = new Bignum();
other.assignUInt64(operand);
addBignum(other);
}
void addBignum(final Bignum other) {
assert (isClamped());
assert (other.isClamped());
// If this has a greater exponent than other append zero-bigits to this.
// After this call exponent_ <= other.exponent_.
align(other);
// There are two possibilities:
// aaaaaaaaaaa 0000 (where the 0s represent a's exponent)
// bbbbb 00000000
// ----------------
// ccccccccccc 0000
// or
// aaaaaaaaaa 0000
// bbbbbbbbb 0000000
// -----------------
// cccccccccccc 0000
// In both cases we might need a carry bigit.
ensureCapacity(1 + Math.max(bigitLength(), other.bigitLength()) - exponent_);
int carry = 0;
int bigit_pos = other.exponent_ - exponent_;
assert (bigit_pos >= 0);
for (int i = 0; i < other.used_digits_; ++i) {
final int sum = bigits_[bigit_pos] + other.bigits_[i] + carry;
bigits_[bigit_pos] = sum & kBigitMask;
carry = sum >>> kBigitSize;
bigit_pos++;
}
while (carry != 0) {
final int sum = bigits_[bigit_pos] + carry;
bigits_[bigit_pos] = sum & kBigitMask;
carry = sum >>> kBigitSize;
bigit_pos++;
}
used_digits_ = Math.max(bigit_pos, used_digits_);
assert (isClamped());
}
void subtractBignum(final Bignum other) {
assert (isClamped());
assert (other.isClamped());
// We require this to be bigger than other.
assert (lessEqual(other, this));
align(other);
final int offset = other.exponent_ - exponent_;
int borrow = 0;
int i;
for (i = 0; i < other.used_digits_; ++i) {
assert ((borrow == 0) || (borrow == 1));
final int difference = bigits_[i + offset] - other.bigits_[i] - borrow;
bigits_[i + offset] = difference & kBigitMask;
borrow = difference >>> (kChunkSize - 1);
}
while (borrow != 0) {
final int difference = bigits_[i + offset] - borrow;
bigits_[i + offset] = difference & kBigitMask;
borrow = difference >>> (kChunkSize - 1);
++i;
}
clamp();
}
void shiftLeft(final int shift_amount) {
if (used_digits_ == 0) return;
exponent_ += shift_amount / kBigitSize;
final int local_shift = shift_amount % kBigitSize;
ensureCapacity(used_digits_ + 1);
bigitsShiftLeft(local_shift);
}
void multiplyByUInt32(final int factor) {
if (factor == 1) return;
if (factor == 0) {
zero();
return;
}
if (used_digits_ == 0) return;
// The product of a bigit with the factor is of size kBigitSize + 32.
// Assert that this number + 1 (for the carry) fits into double int.
assert (kDoubleChunkSize >= kBigitSize + 32 + 1);
long carry = 0;
for (int i = 0; i < used_digits_; ++i) {
final long product = (factor & 0xFFFFFFFFL) * bigits_[i] + carry;
bigits_[i] = (int) (product & kBigitMask);
carry = product >>> kBigitSize;
}
while (carry != 0) {
ensureCapacity(used_digits_ + 1);
bigits_[used_digits_] = (int) (carry & kBigitMask);
used_digits_++;
carry >>>= kBigitSize;
}
}
void multiplyByUInt64(final long factor) {
if (factor == 1) return;
if (factor == 0) {
zero();
return;
}
assert (kBigitSize < 32);
long carry = 0;
final long low = factor & 0xFFFFFFFFL;
final long high = factor >>> 32;
for (int i = 0; i < used_digits_; ++i) {
final long product_low = low * bigits_[i];
final long product_high = high * bigits_[i];
final long tmp = (carry & kBigitMask) + product_low;
bigits_[i] = (int) (tmp & kBigitMask);
carry = (carry >>> kBigitSize) + (tmp >>> kBigitSize) +
(product_high << (32 - kBigitSize));
}
while (carry != 0) {
ensureCapacity(used_digits_ + 1);
bigits_[used_digits_] = (int) (carry & kBigitMask);
used_digits_++;
carry >>>= kBigitSize;
}
}
void multiplyByPowerOfTen(final int exponent) {
final long kFive27 = 0x6765c793fa10079dL;
final int kFive1 = 5;
final int kFive2 = kFive1 * 5;
final int kFive3 = kFive2 * 5;
final int kFive4 = kFive3 * 5;
final int kFive5 = kFive4 * 5;
final int kFive6 = kFive5 * 5;
final int kFive7 = kFive6 * 5;
final int kFive8 = kFive7 * 5;
final int kFive9 = kFive8 * 5;
final int kFive10 = kFive9 * 5;
final int kFive11 = kFive10 * 5;
final int kFive12 = kFive11 * 5;
final int kFive13 = kFive12 * 5;
final int kFive1_to_12[] =
{ kFive1, kFive2, kFive3, kFive4, kFive5, kFive6,
kFive7, kFive8, kFive9, kFive10, kFive11, kFive12 };
assert (exponent >= 0);
if (exponent == 0) return;
if (used_digits_ == 0) return;
// We shift by exponent at the end just before returning.
int remaining_exponent = exponent;
while (remaining_exponent >= 27) {
multiplyByUInt64(kFive27);
remaining_exponent -= 27;
}
while (remaining_exponent >= 13) {
multiplyByUInt32(kFive13);
remaining_exponent -= 13;
}
if (remaining_exponent > 0) {
multiplyByUInt32(kFive1_to_12[remaining_exponent - 1]);
}
shiftLeft(exponent);
}
void square() {
assert (isClamped());
final int product_length = 2 * used_digits_;
ensureCapacity(product_length);
// Comba multiplication: compute each column separately.
// Example: r = a2a1a0 * b2b1b0.
// r = 1 * a0b0 +
// 10 * (a1b0 + a0b1) +
// 100 * (a2b0 + a1b1 + a0b2) +
// 1000 * (a2b1 + a1b2) +
// 10000 * a2b2
//
// In the worst case we have to accumulate nb-digits products of digit*digit.
//
// Assert that the additional number of bits in a DoubleChunk are enough to
// sum up used_digits of Bigit*Bigit.
if ((1L << (2 * (kChunkSize - kBigitSize))) <= used_digits_) {
throw new RuntimeException("unimplemented");
}
long accumulator = 0;
// First shift the digits so we don't overwrite them.
final int copy_offset = used_digits_;
for (int i = 0; i < used_digits_; ++i) {
bigits_[copy_offset + i] = bigits_[i];
}
// We have two loops to avoid some 'if's in the loop.
for (int i = 0; i < used_digits_; ++i) {
// Process temporary digit i with power i.
// The sum of the two indices must be equal to i.
int bigit_index1 = i;
int bigit_index2 = 0;
// Sum all of the sub-products.
while (bigit_index1 >= 0) {
final int int1 = bigits_[copy_offset + bigit_index1];
final int int2 = bigits_[copy_offset + bigit_index2];
accumulator += ((long) int1) * int2;
bigit_index1--;
bigit_index2++;
}
bigits_[i] = (int) (accumulator & kBigitMask);
accumulator >>>= kBigitSize;
}
for (int i = used_digits_; i < product_length; ++i) {
int bigit_index1 = used_digits_ - 1;
int bigit_index2 = i - bigit_index1;
// Invariant: sum of both indices is again equal to i.
// Inner loop runs 0 times on last iteration, emptying accumulator.
while (bigit_index2 < used_digits_) {
final int int1 = bigits_[copy_offset + bigit_index1];
final int int2 = bigits_[copy_offset + bigit_index2];
accumulator += ((long) int1) * int2;
bigit_index1--;
bigit_index2++;
}
// The overwritten bigits_[i] will never be read in further loop iterations,
// because bigit_index1 and bigit_index2 are always greater
// than i - used_digits_.
bigits_[i] = (int) (accumulator & kBigitMask);
accumulator >>>= kBigitSize;
}
// Since the result was guaranteed to lie inside the number the
// accumulator must be 0 now.
assert (accumulator == 0);
// Don't forget to update the used_digits and the exponent.
used_digits_ = product_length;
exponent_ *= 2;
clamp();
}
void assignPowerUInt16(int base, final int power_exponent) {
assert (base != 0);
assert (power_exponent >= 0);
if (power_exponent == 0) {
assignUInt16((char) 1);
return;
}
zero();
int shifts = 0;
// We expect base to be in range 2-32, and most often to be 10.
// It does not make much sense to implement different algorithms for counting
// the bits.
while ((base & 1) == 0) {
base >>>= 1;
shifts++;
}
int bit_size = 0;
int tmp_base = base;
while (tmp_base != 0) {
tmp_base >>>= 1;
bit_size++;
}
final int final_size = bit_size * power_exponent;
// 1 extra bigit for the shifting, and one for rounded final_size.
ensureCapacity(final_size / kBigitSize + 2);
// Left to Right exponentiation.
int mask = 1;
while (power_exponent >= mask) mask <<= 1;
// The mask is now pointing to the bit above the most significant 1-bit of
// power_exponent.
// Get rid of first 1-bit;
mask >>>= 2;
long this_value = base;
boolean delayed_multipliciation = false;
final long max_32bits = 0xFFFFFFFFL;
while (mask != 0 && this_value <= max_32bits) {
this_value = this_value * this_value;
// Verify that there is enough space in this_value to perform the
// multiplication. The first bit_size bits must be 0.
if ((power_exponent & mask) != 0) {
final long base_bits_mask =
~((1L << (64 - bit_size)) - 1);
final boolean high_bits_zero = (this_value & base_bits_mask) == 0;
if (high_bits_zero) {
this_value *= base;
} else {
delayed_multipliciation = true;
}
}
mask >>>= 1;
}
assignUInt64(this_value);
if (delayed_multipliciation) {
multiplyByUInt32(base);
}
// Now do the same thing as a bignum.
while (mask != 0) {
square();
if ((power_exponent & mask) != 0) {
multiplyByUInt32(base);
}
mask >>>= 1;
}
// And finally add the saved shifts.
shiftLeft(shifts * power_exponent);
}
// Precondition: this/other < 16bit.
char divideModuloIntBignum(final Bignum other) {
assert (isClamped());
assert (other.isClamped());
assert (other.used_digits_ > 0);
// Easy case: if we have less digits than the divisor than the result is 0.
// Note: this handles the case where this == 0, too.
if (bigitLength() < other.bigitLength()) {
return 0;
}
align(other);
char result = 0;
// Start by removing multiples of 'other' until both numbers have the same
// number of digits.
while (bigitLength() > other.bigitLength()) {
// This naive approach is extremely inefficient if `this` divided by other
// is big. This function is implemented for doubleToString where
// the result should be small (less than 10).
assert (other.bigits_[other.used_digits_ - 1] >= ((1 << kBigitSize) / 16));
assert (bigits_[used_digits_ - 1] < 0x10000);
// Remove the multiples of the first digit.
// Example this = 23 and other equals 9. -> Remove 2 multiples.
result += (bigits_[used_digits_ - 1]);
subtractTimes(other, bigits_[used_digits_ - 1]);
}
assert (bigitLength() == other.bigitLength());
// Both bignums are at the same length now.
// Since other has more than 0 digits we know that the access to
// bigits_[used_digits_ - 1] is safe.
final int this_bigit = bigits_[used_digits_ - 1];
final int other_bigit = other.bigits_[other.used_digits_ - 1];
if (other.used_digits_ == 1) {
// Shortcut for easy (and common) case.
final int quotient = Integer.divideUnsigned(this_bigit, other_bigit);
bigits_[used_digits_ - 1] = this_bigit - other_bigit * quotient;
assert (Integer.compareUnsigned(quotient, 0x10000) < 0);
result += quotient;
clamp();
return result;
}
final int division_estimate = Integer.divideUnsigned(this_bigit, (other_bigit + 1));
assert (Integer.compareUnsigned(division_estimate, 0x10000) < 0);
result += division_estimate;
subtractTimes(other, division_estimate);
if (other_bigit * (division_estimate + 1) > this_bigit) {
// No need to even try to subtract. Even if other's remaining digits were 0
// another subtraction would be too much.
return result;
}
while (lessEqual(other, this)) {
subtractBignum(other);
result++;
}
return result;
}
static int sizeInHexChars(int number) {
assert (number > 0);
int result = 0;
while (number != 0) {
number >>>= 4;
result++;
}
return result;
}
static char hexCharOfValue(final int value) {
assert (0 <= value && value <= 16);
if (value < 10) return (char) (value + '0');
return (char) (value - 10 + 'A');
}
String toHexString() {
assert (isClamped());
// Each bigit must be printable as separate hex-character.
assert (kBigitSize % 4 == 0);
final int kHexCharsPerBigit = kBigitSize / 4;
if (used_digits_ == 0) {
return "0";
}
final int needed_chars = (bigitLength() - 1) * kHexCharsPerBigit +
sizeInHexChars(bigits_[used_digits_ - 1]);
final StringBuilder buffer = new StringBuilder(needed_chars);
buffer.setLength(needed_chars);
int string_index = needed_chars - 1;
for (int i = 0; i < exponent_; ++i) {
for (int j = 0; j < kHexCharsPerBigit; ++j) {
buffer.setCharAt(string_index--, '0');
}
}
for (int i = 0; i < used_digits_ - 1; ++i) {
int current_bigit = bigits_[i];
for (int j = 0; j < kHexCharsPerBigit; ++j) {
buffer.setCharAt(string_index--, hexCharOfValue(current_bigit & 0xF));
current_bigit >>>= 4;
}
}
// And finally the last bigit.
int most_significant_bigit = bigits_[used_digits_ - 1];
while (most_significant_bigit != 0) {
buffer.setCharAt(string_index--, hexCharOfValue(most_significant_bigit & 0xF));
most_significant_bigit >>>= 4;
}
return buffer.toString();
}
int bigitAt(final int index) {
if (index >= bigitLength()) return 0;
if (index < exponent_) return 0;
return bigits_[index - exponent_];
}
static int compare(final Bignum a, final Bignum b) {
assert (a.isClamped());
assert (b.isClamped());
final int bigit_length_a = a.bigitLength();
final int bigit_length_b = b.bigitLength();
if (bigit_length_a < bigit_length_b) return -1;
if (bigit_length_a > bigit_length_b) return +1;
for (int i = bigit_length_a - 1; i >= Math.min(a.exponent_, b.exponent_); --i) {
final int bigit_a = a.bigitAt(i);
final int bigit_b = b.bigitAt(i);
if (bigit_a < bigit_b) return -1;
if (bigit_a > bigit_b) return +1;
// Otherwise they are equal up to this digit. Try the next digit.
}
return 0;
}
static int plusCompare(final Bignum a, final Bignum b, final Bignum c) {
assert (a.isClamped());
assert (b.isClamped());
assert (c.isClamped());
if (a.bigitLength() < b.bigitLength()) {
return plusCompare(b, a, c);
}
if (a.bigitLength() + 1 < c.bigitLength()) return -1;
if (a.bigitLength() > c.bigitLength()) return +1;
// The exponent encodes 0-bigits. So if there are more 0-digits in 'a' than
// 'b' has digits, then the bigit-length of 'a'+'b' must be equal to the one
// of 'a'.
if (a.exponent_ >= b.bigitLength() && a.bigitLength() < c.bigitLength()) {
return -1;
}
int borrow = 0;
// Starting at min_exponent all digits are == 0. So no need to compare them.
final int min_exponent = Math.min(Math.min(a.exponent_, b.exponent_), c.exponent_);
for (int i = c.bigitLength() - 1; i >= min_exponent; --i) {
final int int_a = a.bigitAt(i);
final int int_b = b.bigitAt(i);
final int int_c = c.bigitAt(i);
final int sum = int_a + int_b;
if (sum > int_c + borrow) {
return +1;
} else {
borrow = int_c + borrow - sum;
if (borrow > 1) return -1;
borrow <<= kBigitSize;
}
}
if (borrow == 0) return 0;
return -1;
}
void clamp() {
while (used_digits_ > 0 && bigits_[used_digits_ - 1] == 0) {
used_digits_--;
}
if (used_digits_ == 0) {
// Zero.
exponent_ = 0;
}
}
boolean isClamped() {
return used_digits_ == 0 || bigits_[used_digits_ - 1] != 0;
}
void zero() {
for (int i = 0; i < used_digits_; ++i) {
bigits_[i] = 0;
}
used_digits_ = 0;
exponent_ = 0;
}
void align(final Bignum other) {
if (exponent_ > other.exponent_) {
// If "X" represents a "hidden" digit (by the exponent) then we are in the
// following case (a == this, b == other):
// a: aaaaaaXXXX or a: aaaaaXXX
// b: bbbbbbX b: bbbbbbbbXX
// We replace some of the hidden digits (X) of a with 0 digits.
// a: aaaaaa000X or a: aaaaa0XX
final int zero_digits = exponent_ - other.exponent_;
ensureCapacity(used_digits_ + zero_digits);
for (int i = used_digits_ - 1; i >= 0; --i) {
bigits_[i + zero_digits] = bigits_[i];
}
for (int i = 0; i < zero_digits; ++i) {
bigits_[i] = 0;
}
used_digits_ += zero_digits;
exponent_ -= zero_digits;
assert (used_digits_ >= 0);
assert (exponent_ >= 0);
}
}
void bigitsShiftLeft(final int shift_amount) {
assert (shift_amount < kBigitSize);
assert (shift_amount >= 0);
int carry = 0;
for (int i = 0; i < used_digits_; ++i) {
final int new_carry = bigits_[i] >>> (kBigitSize - shift_amount);
bigits_[i] = ((bigits_[i] << shift_amount) + carry) & kBigitMask;
carry = new_carry;
}
if (carry != 0) {
bigits_[used_digits_] = carry;
used_digits_++;
}
}
void subtractTimes(final Bignum other, final int factor) {
assert (exponent_ <= other.exponent_);
if (factor < 3) {
for (int i = 0; i < factor; ++i) {
subtractBignum(other);
}
return;
}
int borrow = 0;
final int exponent_diff = other.exponent_ - exponent_;
for (int i = 0; i < other.used_digits_; ++i) {
final long product = ((long) factor) * other.bigits_[i];
final long remove = borrow + product;
final int difference = bigits_[i + exponent_diff] - (int) (remove & kBigitMask);
bigits_[i + exponent_diff] = difference & kBigitMask;
borrow = (int) ((difference >>> (kChunkSize - 1)) +
(remove >>> kBigitSize));
}
for (int i = other.used_digits_ + exponent_diff; i < used_digits_; ++i) {
if (borrow == 0) return;
final int difference = bigits_[i] - borrow;
bigits_[i] = difference & kBigitMask;
borrow = difference >>> (kChunkSize - 1);
}
clamp();
}
@Override
public String toString() {
return "Bignum" + Arrays.toString(bigits_);
}
}

@ -0,0 +1,647 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jdk.nashorn.internal.runtime.doubleconv;
// Dtoa implementation based on our own Bignum implementation, supporting
// all conversion modes but slightly slower than the specialized implementations.
class BignumDtoa {
private static int normalizedExponent(long significand, int exponent) {
assert (significand != 0);
while ((significand & IeeeDouble.kHiddenBit) == 0) {
significand = significand << 1;
exponent = exponent - 1;
}
return exponent;
}
// Converts the given double 'v' to ascii.
// The result should be interpreted as buffer * 10^(point-length).
// The buffer will be null-terminated.
//
// The input v must be > 0 and different from NaN, and Infinity.
//
// The output depends on the given mode:
// - SHORTEST: produce the least amount of digits for which the internal
// identity requirement is still satisfied. If the digits are printed
// (together with the correct exponent) then reading this number will give
// 'v' again. The buffer will choose the representation that is closest to
// 'v'. If there are two at the same distance, than the number is round up.
// In this mode the 'requested_digits' parameter is ignored.
// - FIXED: produces digits necessary to print a given number with
// 'requested_digits' digits after the decimal point. The produced digits
// might be too short in which case the caller has to fill the gaps with '0's.
// Example: toFixed(0.001, 5) is allowed to return buffer="1", point=-2.
// Halfway cases are rounded up. The call toFixed(0.15, 2) thus returns
// buffer="2", point=0.
// Note: the length of the returned buffer has no meaning wrt the significance
// of its digits. That is, just because it contains '0's does not mean that
// any other digit would not satisfy the internal identity requirement.
// - PRECISION: produces 'requested_digits' where the first digit is not '0'.
// Even though the length of produced digits usually equals
// 'requested_digits', the function is allowed to return fewer digits, in
// which case the caller has to fill the missing digits with '0's.
// Halfway cases are again rounded up.
// 'BignumDtoa' expects the given buffer to be big enough to hold all digits
// and a terminating null-character.
static void bignumDtoa(final double v, final DtoaMode mode, final int requested_digits,
final DtoaBuffer buffer) {
assert (v > 0);
assert (!IeeeDouble.isSpecial(IeeeDouble.doubleToLong(v)));
final long significand;
final int exponent;
final boolean lower_boundary_is_closer;
final long l = IeeeDouble.doubleToLong(v);
significand = IeeeDouble.significand(l);
exponent = IeeeDouble.exponent(l);
lower_boundary_is_closer = IeeeDouble.lowerBoundaryIsCloser(l);
final boolean need_boundary_deltas = mode == DtoaMode.SHORTEST;
final boolean is_even = (significand & 1) == 0;
assert (significand != 0);
final int normalizedExponent = normalizedExponent(significand, exponent);
// estimated_power might be too low by 1.
final int estimated_power = estimatePower(normalizedExponent);
// Shortcut for Fixed.
// The requested digits correspond to the digits after the point. If the
// number is much too small, then there is no need in trying to get any
// digits.
if (mode == DtoaMode.FIXED && -estimated_power - 1 > requested_digits) {
buffer.reset();
// Set decimal-point to -requested_digits. This is what Gay does.
// Note that it should not have any effect anyways since the string is
// empty.
buffer.decimalPoint = -requested_digits;
return;
}
final Bignum numerator = new Bignum();
final Bignum denominator = new Bignum();
final Bignum delta_minus = new Bignum();
final Bignum delta_plus = new Bignum();
// Make sure the bignum can grow large enough. The smallest double equals
// 4e-324. In this case the denominator needs fewer than 324*4 binary digits.
// The maximum double is 1.7976931348623157e308 which needs fewer than
// 308*4 binary digits.
assert (Bignum.kMaxSignificantBits >= 324*4);
initialScaledStartValues(significand, exponent, lower_boundary_is_closer,
estimated_power, need_boundary_deltas,
numerator, denominator,
delta_minus, delta_plus);
// We now have v = (numerator / denominator) * 10^estimated_power.
buffer.decimalPoint = fixupMultiply10(estimated_power, is_even,
numerator, denominator,
delta_minus, delta_plus);
// We now have v = (numerator / denominator) * 10^(decimal_point-1), and
// 1 <= (numerator + delta_plus) / denominator < 10
switch (mode) {
case SHORTEST:
generateShortestDigits(numerator, denominator,
delta_minus, delta_plus,
is_even, buffer);
break;
case FIXED:
bignumToFixed(requested_digits,
numerator, denominator,
buffer);
break;
case PRECISION:
generateCountedDigits(requested_digits,
numerator, denominator,
buffer);
break;
default:
throw new RuntimeException();
}
}
// The procedure starts generating digits from the left to the right and stops
// when the generated digits yield the shortest decimal representation of v. A
// decimal representation of v is a number lying closer to v than to any other
// double, so it converts to v when read.
//
// This is true if d, the decimal representation, is between m- and m+, the
// upper and lower boundaries. d must be strictly between them if !is_even.
// m- := (numerator - delta_minus) / denominator
// m+ := (numerator + delta_plus) / denominator
//
// Precondition: 0 <= (numerator+delta_plus) / denominator < 10.
// If 1 <= (numerator+delta_plus) / denominator < 10 then no leading 0 digit
// will be produced. This should be the standard precondition.
static void generateShortestDigits(final Bignum numerator, final Bignum denominator,
final Bignum delta_minus, Bignum delta_plus,
final boolean is_even,
final DtoaBuffer buffer) {
// Small optimization: if delta_minus and delta_plus are the same just reuse
// one of the two bignums.
if (Bignum.equal(delta_minus, delta_plus)) {
delta_plus = delta_minus;
}
for (;;) {
final char digit;
digit = numerator.divideModuloIntBignum(denominator);
assert (digit <= 9); // digit is a uint16_t and therefore always positive.
// digit = numerator / denominator (integer division).
// numerator = numerator % denominator.
buffer.append((char) (digit + '0'));
// Can we stop already?
// If the remainder of the division is less than the distance to the lower
// boundary we can stop. In this case we simply round down (discarding the
// remainder).
// Similarly we test if we can round up (using the upper boundary).
final boolean in_delta_room_minus;
final boolean in_delta_room_plus;
if (is_even) {
in_delta_room_minus = Bignum.lessEqual(numerator, delta_minus);
} else {
in_delta_room_minus = Bignum.less(numerator, delta_minus);
}
if (is_even) {
in_delta_room_plus =
Bignum.plusCompare(numerator, delta_plus, denominator) >= 0;
} else {
in_delta_room_plus =
Bignum.plusCompare(numerator, delta_plus, denominator) > 0;
}
if (!in_delta_room_minus && !in_delta_room_plus) {
// Prepare for next iteration.
numerator.times10();
delta_minus.times10();
// We optimized delta_plus to be equal to delta_minus (if they share the
// same value). So don't multiply delta_plus if they point to the same
// object.
if (delta_minus != delta_plus) {
delta_plus.times10();
}
} else if (in_delta_room_minus && in_delta_room_plus) {
// Let's see if 2*numerator < denominator.
// If yes, then the next digit would be < 5 and we can round down.
final int compare = Bignum.plusCompare(numerator, numerator, denominator);
if (compare < 0) {
// Remaining digits are less than .5. -> Round down (== do nothing).
} else if (compare > 0) {
// Remaining digits are more than .5 of denominator. -> Round up.
// Note that the last digit could not be a '9' as otherwise the whole
// loop would have stopped earlier.
// We still have an assert here in case the preconditions were not
// satisfied.
assert (buffer.chars[buffer.length - 1] != '9');
buffer.chars[buffer.length - 1]++;
} else {
// Halfway case.
// TODO(floitsch): need a way to solve half-way cases.
// For now let's round towards even (since this is what Gay seems to
// do).
if ((buffer.chars[buffer.length - 1] - '0') % 2 == 0) {
// Round down => Do nothing.
} else {
assert (buffer.chars[buffer.length - 1] != '9');
buffer.chars[buffer.length - 1]++;
}
}
return;
} else if (in_delta_room_minus) {
// Round down (== do nothing).
return;
} else { // in_delta_room_plus
// Round up.
// Note again that the last digit could not be '9' since this would have
// stopped the loop earlier.
// We still have an ASSERT here, in case the preconditions were not
// satisfied.
assert (buffer.chars[buffer.length -1] != '9');
buffer.chars[buffer.length - 1]++;
return;
}
}
}
// Let v = numerator / denominator < 10.
// Then we generate 'count' digits of d = x.xxxxx... (without the decimal point)
// from left to right. Once 'count' digits have been produced we decide wether
// to round up or down. Remainders of exactly .5 round upwards. Numbers such
// as 9.999999 propagate a carry all the way, and change the
// exponent (decimal_point), when rounding upwards.
static void generateCountedDigits(final int count,
final Bignum numerator, final Bignum denominator,
final DtoaBuffer buffer) {
assert (count >= 0);
for (int i = 0; i < count - 1; ++i) {
final char digit;
digit = numerator.divideModuloIntBignum(denominator);
assert (digit <= 9); // digit is a uint16_t and therefore always positive.
// digit = numerator / denominator (integer division).
// numerator = numerator % denominator.
buffer.chars[i] = (char)(digit + '0');
// Prepare for next iteration.
numerator.times10();
}
// Generate the last digit.
char digit;
digit = numerator.divideModuloIntBignum(denominator);
if (Bignum.plusCompare(numerator, numerator, denominator) >= 0) {
digit++;
}
assert (digit <= 10);
buffer.chars[count - 1] = (char) (digit + '0');
// Correct bad digits (in case we had a sequence of '9's). Propagate the
// carry until we hat a non-'9' or til we reach the first digit.
for (int i = count - 1; i > 0; --i) {
if (buffer.chars[i] != '0' + 10) break;
buffer.chars[i] = '0';
buffer.chars[i - 1]++;
}
if (buffer.chars[0] == '0' + 10) {
// Propagate a carry past the top place.
buffer.chars[0] = '1';
buffer.decimalPoint++;
}
buffer.length = count;
}
// Generates 'requested_digits' after the decimal point. It might omit
// trailing '0's. If the input number is too small then no digits at all are
// generated (ex.: 2 fixed digits for 0.00001).
//
// Input verifies: 1 <= (numerator + delta) / denominator < 10.
static void bignumToFixed(final int requested_digits,
final Bignum numerator, final Bignum denominator,
final DtoaBuffer buffer) {
// Note that we have to look at more than just the requested_digits, since
// a number could be rounded up. Example: v=0.5 with requested_digits=0.
// Even though the power of v equals 0 we can't just stop here.
if (-buffer.decimalPoint > requested_digits) {
// The number is definitively too small.
// Ex: 0.001 with requested_digits == 1.
// Set decimal-decimalPoint to -requested_digits. This is what Gay does.
// Note that it should not have any effect anyways since the string is
// empty.
buffer.decimalPoint = -requested_digits;
buffer.length = 0;
// return;
} else if (-buffer.decimalPoint == requested_digits) {
// We only need to verify if the number rounds down or up.
// Ex: 0.04 and 0.06 with requested_digits == 1.
assert (buffer.decimalPoint == -requested_digits);
// Initially the fraction lies in range (1, 10]. Multiply the denominator
// by 10 so that we can compare more easily.
denominator.times10();
if (Bignum.plusCompare(numerator, numerator, denominator) >= 0) {
// If the fraction is >= 0.5 then we have to include the rounded
// digit.
buffer.chars[0] = '1';
buffer.length = 1;
buffer.decimalPoint++;
} else {
// Note that we caught most of similar cases earlier.
buffer.length = 0;
}
// return;
} else {
// The requested digits correspond to the digits after the point.
// The variable 'needed_digits' includes the digits before the point.
final int needed_digits = buffer.decimalPoint + requested_digits;
generateCountedDigits(needed_digits,
numerator, denominator,
buffer);
}
}
// Returns an estimation of k such that 10^(k-1) <= v < 10^k where
// v = f * 2^exponent and 2^52 <= f < 2^53.
// v is hence a normalized double with the given exponent. The output is an
// approximation for the exponent of the decimal approimation .digits * 10^k.
//
// The result might undershoot by 1 in which case 10^k <= v < 10^k+1.
// Note: this property holds for v's upper boundary m+ too.
// 10^k <= m+ < 10^k+1.
// (see explanation below).
//
// Examples:
// EstimatePower(0) => 16
// EstimatePower(-52) => 0
//
// Note: e >= 0 => EstimatedPower(e) > 0. No similar claim can be made for e<0.
static int estimatePower(final int exponent) {
// This function estimates log10 of v where v = f*2^e (with e == exponent).
// Note that 10^floor(log10(v)) <= v, but v <= 10^ceil(log10(v)).
// Note that f is bounded by its container size. Let p = 53 (the double's
// significand size). Then 2^(p-1) <= f < 2^p.
//
// Given that log10(v) == log2(v)/log2(10) and e+(len(f)-1) is quite close
// to log2(v) the function is simplified to (e+(len(f)-1)/log2(10)).
// The computed number undershoots by less than 0.631 (when we compute log3
// and not log10).
//
// Optimization: since we only need an approximated result this computation
// can be performed on 64 bit integers. On x86/x64 architecture the speedup is
// not really measurable, though.
//
// Since we want to avoid overshooting we decrement by 1e10 so that
// floating-point imprecisions don't affect us.
//
// Explanation for v's boundary m+: the computation takes advantage of
// the fact that 2^(p-1) <= f < 2^p. Boundaries still satisfy this requirement
// (even for denormals where the delta can be much more important).
final double k1Log10 = 0.30102999566398114; // 1/lg(10)
// For doubles len(f) == 53 (don't forget the hidden bit).
final int kSignificandSize = IeeeDouble.kSignificandSize;
final double estimate = Math.ceil((exponent + kSignificandSize - 1) * k1Log10 - 1e-10);
return (int) estimate;
}
// See comments for InitialScaledStartValues.
static void initialScaledStartValuesPositiveExponent(
final long significand, final int exponent,
final int estimated_power, final boolean need_boundary_deltas,
final Bignum numerator, final Bignum denominator,
final Bignum delta_minus, final Bignum delta_plus) {
// A positive exponent implies a positive power.
assert (estimated_power >= 0);
// Since the estimated_power is positive we simply multiply the denominator
// by 10^estimated_power.
// numerator = v.
numerator.assignUInt64(significand);
numerator.shiftLeft(exponent);
// denominator = 10^estimated_power.
denominator.assignPowerUInt16(10, estimated_power);
if (need_boundary_deltas) {
// Introduce a common denominator so that the deltas to the boundaries are
// integers.
denominator.shiftLeft(1);
numerator.shiftLeft(1);
// Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
// denominator (of 2) delta_plus equals 2^e.
delta_plus.assignUInt16((char) 1);
delta_plus.shiftLeft(exponent);
// Same for delta_minus. The adjustments if f == 2^p-1 are done later.
delta_minus.assignUInt16((char) 1);
delta_minus.shiftLeft(exponent);
}
}
// See comments for InitialScaledStartValues
static void initialScaledStartValuesNegativeExponentPositivePower(
final long significand, final int exponent,
final int estimated_power, final boolean need_boundary_deltas,
final Bignum numerator, final Bignum denominator,
final Bignum delta_minus, final Bignum delta_plus) {
// v = f * 2^e with e < 0, and with estimated_power >= 0.
// This means that e is close to 0 (have a look at how estimated_power is
// computed).
// numerator = significand
// since v = significand * 2^exponent this is equivalent to
// numerator = v * / 2^-exponent
numerator.assignUInt64(significand);
// denominator = 10^estimated_power * 2^-exponent (with exponent < 0)
denominator.assignPowerUInt16(10, estimated_power);
denominator.shiftLeft(-exponent);
if (need_boundary_deltas) {
// Introduce a common denominator so that the deltas to the boundaries are
// integers.
denominator.shiftLeft(1);
numerator.shiftLeft(1);
// Let v = f * 2^e, then m+ - v = 1/2 * 2^e; With the common
// denominator (of 2) delta_plus equals 2^e.
// Given that the denominator already includes v's exponent the distance
// to the boundaries is simply 1.
delta_plus.assignUInt16((char) 1);
// Same for delta_minus. The adjustments if f == 2^p-1 are done later.
delta_minus.assignUInt16((char) 1);
}
}
// See comments for InitialScaledStartValues
static void initialScaledStartValuesNegativeExponentNegativePower(
final long significand, final int exponent,
final int estimated_power, final boolean need_boundary_deltas,
final Bignum numerator, final Bignum denominator,
final Bignum delta_minus, final Bignum delta_plus) {
// Instead of multiplying the denominator with 10^estimated_power we
// multiply all values (numerator and deltas) by 10^-estimated_power.
// Use numerator as temporary container for power_ten.
final Bignum power_ten = numerator;
power_ten.assignPowerUInt16(10, -estimated_power);
if (need_boundary_deltas) {
// Since power_ten == numerator we must make a copy of 10^estimated_power
// before we complete the computation of the numerator.
// delta_plus = delta_minus = 10^estimated_power
delta_plus.assignBignum(power_ten);
delta_minus.assignBignum(power_ten);
}
// numerator = significand * 2 * 10^-estimated_power
// since v = significand * 2^exponent this is equivalent to
// numerator = v * 10^-estimated_power * 2 * 2^-exponent.
// Remember: numerator has been abused as power_ten. So no need to assign it
// to itself.
assert (numerator == power_ten);
numerator.multiplyByUInt64(significand);
// denominator = 2 * 2^-exponent with exponent < 0.
denominator.assignUInt16((char) 1);
denominator.shiftLeft(-exponent);
if (need_boundary_deltas) {
// Introduce a common denominator so that the deltas to the boundaries are
// integers.
numerator.shiftLeft(1);
denominator.shiftLeft(1);
// With this shift the boundaries have their correct value, since
// delta_plus = 10^-estimated_power, and
// delta_minus = 10^-estimated_power.
// These assignments have been done earlier.
// The adjustments if f == 2^p-1 (lower boundary is closer) are done later.
}
}
// Let v = significand * 2^exponent.
// Computes v / 10^estimated_power exactly, as a ratio of two bignums, numerator
// and denominator. The functions GenerateShortestDigits and
// GenerateCountedDigits will then convert this ratio to its decimal
// representation d, with the required accuracy.
// Then d * 10^estimated_power is the representation of v.
// (Note: the fraction and the estimated_power might get adjusted before
// generating the decimal representation.)
//
// The initial start values consist of:
// - a scaled numerator: s.t. numerator/denominator == v / 10^estimated_power.
// - a scaled (common) denominator.
// optionally (used by GenerateShortestDigits to decide if it has the shortest
// decimal converting back to v):
// - v - m-: the distance to the lower boundary.
// - m+ - v: the distance to the upper boundary.
//
// v, m+, m-, and therefore v - m- and m+ - v all share the same denominator.
//
// Let ep == estimated_power, then the returned values will satisfy:
// v / 10^ep = numerator / denominator.
// v's boundarys m- and m+:
// m- / 10^ep == v / 10^ep - delta_minus / denominator
// m+ / 10^ep == v / 10^ep + delta_plus / denominator
// Or in other words:
// m- == v - delta_minus * 10^ep / denominator;
// m+ == v + delta_plus * 10^ep / denominator;
//
// Since 10^(k-1) <= v < 10^k (with k == estimated_power)
// or 10^k <= v < 10^(k+1)
// we then have 0.1 <= numerator/denominator < 1
// or 1 <= numerator/denominator < 10
//
// It is then easy to kickstart the digit-generation routine.
//
// The boundary-deltas are only filled if the mode equals BIGNUM_DTOA_SHORTEST
// or BIGNUM_DTOA_SHORTEST_SINGLE.
static void initialScaledStartValues(final long significand,
final int exponent,
final boolean lower_boundary_is_closer,
final int estimated_power,
final boolean need_boundary_deltas,
final Bignum numerator,
final Bignum denominator,
final Bignum delta_minus,
final Bignum delta_plus) {
if (exponent >= 0) {
initialScaledStartValuesPositiveExponent(
significand, exponent, estimated_power, need_boundary_deltas,
numerator, denominator, delta_minus, delta_plus);
} else if (estimated_power >= 0) {
initialScaledStartValuesNegativeExponentPositivePower(
significand, exponent, estimated_power, need_boundary_deltas,
numerator, denominator, delta_minus, delta_plus);
} else {
initialScaledStartValuesNegativeExponentNegativePower(
significand, exponent, estimated_power, need_boundary_deltas,
numerator, denominator, delta_minus, delta_plus);
}
if (need_boundary_deltas && lower_boundary_is_closer) {
// The lower boundary is closer at half the distance of "normal" numbers.
// Increase the common denominator and adapt all but the delta_minus.
denominator.shiftLeft(1); // *2
numerator.shiftLeft(1); // *2
delta_plus.shiftLeft(1); // *2
}
}
// This routine multiplies numerator/denominator so that its values lies in the
// range 1-10. That is after a call to this function we have:
// 1 <= (numerator + delta_plus) /denominator < 10.
// Let numerator the input before modification and numerator' the argument
// after modification, then the output-parameter decimal_point is such that
// numerator / denominator * 10^estimated_power ==
// numerator' / denominator' * 10^(decimal_point - 1)
// In some cases estimated_power was too low, and this is already the case. We
// then simply adjust the power so that 10^(k-1) <= v < 10^k (with k ==
// estimated_power) but do not touch the numerator or denominator.
// Otherwise the routine multiplies the numerator and the deltas by 10.
static int fixupMultiply10(final int estimated_power, final boolean is_even,
final Bignum numerator, final Bignum denominator,
final Bignum delta_minus, final Bignum delta_plus) {
final boolean in_range;
final int decimal_point;
if (is_even) {
// For IEEE doubles half-way cases (in decimal system numbers ending with 5)
// are rounded to the closest floating-point number with even significand.
in_range = Bignum.plusCompare(numerator, delta_plus, denominator) >= 0;
} else {
in_range = Bignum.plusCompare(numerator, delta_plus, denominator) > 0;
}
if (in_range) {
// Since numerator + delta_plus >= denominator we already have
// 1 <= numerator/denominator < 10. Simply update the estimated_power.
decimal_point = estimated_power + 1;
} else {
decimal_point = estimated_power;
numerator.times10();
if (Bignum.equal(delta_minus, delta_plus)) {
delta_minus.times10();
delta_plus.assignBignum(delta_minus);
} else {
delta_minus.times10();
delta_plus.times10();
}
}
return decimal_point;
}
}

@ -0,0 +1,204 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jdk.nashorn.internal.runtime.doubleconv;
public class CachedPowers {
static class CachedPower {
final private long significand;
final private int binaryExponent;
final private int decimalExponent;
CachedPower(final long significand, final int binaryExponent, final int decimalExponent) {
this.significand = significand;
this.binaryExponent = binaryExponent;
this.decimalExponent = decimalExponent;
}
}
static private final CachedPower[] kCachedPowers = {
new CachedPower(0xfa8fd5a0081c0288L, -1220, -348),
new CachedPower(0xbaaee17fa23ebf76L, -1193, -340),
new CachedPower(0x8b16fb203055ac76L, -1166, -332),
new CachedPower(0xcf42894a5dce35eaL, -1140, -324),
new CachedPower(0x9a6bb0aa55653b2dL, -1113, -316),
new CachedPower(0xe61acf033d1a45dfL, -1087, -308),
new CachedPower(0xab70fe17c79ac6caL, -1060, -300),
new CachedPower(0xff77b1fcbebcdc4fL, -1034, -292),
new CachedPower(0xbe5691ef416bd60cL, -1007, -284),
new CachedPower(0x8dd01fad907ffc3cL, -980, -276),
new CachedPower(0xd3515c2831559a83L, -954, -268),
new CachedPower(0x9d71ac8fada6c9b5L, -927, -260),
new CachedPower(0xea9c227723ee8bcbL, -901, -252),
new CachedPower(0xaecc49914078536dL, -874, -244),
new CachedPower(0x823c12795db6ce57L, -847, -236),
new CachedPower(0xc21094364dfb5637L, -821, -228),
new CachedPower(0x9096ea6f3848984fL, -794, -220),
new CachedPower(0xd77485cb25823ac7L, -768, -212),
new CachedPower(0xa086cfcd97bf97f4L, -741, -204),
new CachedPower(0xef340a98172aace5L, -715, -196),
new CachedPower(0xb23867fb2a35b28eL, -688, -188),
new CachedPower(0x84c8d4dfd2c63f3bL, -661, -180),
new CachedPower(0xc5dd44271ad3cdbaL, -635, -172),
new CachedPower(0x936b9fcebb25c996L, -608, -164),
new CachedPower(0xdbac6c247d62a584L, -582, -156),
new CachedPower(0xa3ab66580d5fdaf6L, -555, -148),
new CachedPower(0xf3e2f893dec3f126L, -529, -140),
new CachedPower(0xb5b5ada8aaff80b8L, -502, -132),
new CachedPower(0x87625f056c7c4a8bL, -475, -124),
new CachedPower(0xc9bcff6034c13053L, -449, -116),
new CachedPower(0x964e858c91ba2655L, -422, -108),
new CachedPower(0xdff9772470297ebdL, -396, -100),
new CachedPower(0xa6dfbd9fb8e5b88fL, -369, -92),
new CachedPower(0xf8a95fcf88747d94L, -343, -84),
new CachedPower(0xb94470938fa89bcfL, -316, -76),
new CachedPower(0x8a08f0f8bf0f156bL, -289, -68),
new CachedPower(0xcdb02555653131b6L, -263, -60),
new CachedPower(0x993fe2c6d07b7facL, -236, -52),
new CachedPower(0xe45c10c42a2b3b06L, -210, -44),
new CachedPower(0xaa242499697392d3L, -183, -36),
new CachedPower(0xfd87b5f28300ca0eL, -157, -28),
new CachedPower(0xbce5086492111aebL, -130, -20),
new CachedPower(0x8cbccc096f5088ccL, -103, -12),
new CachedPower(0xd1b71758e219652cL, -77, -4),
new CachedPower(0x9c40000000000000L, -50, 4),
new CachedPower(0xe8d4a51000000000L, -24, 12),
new CachedPower(0xad78ebc5ac620000L, 3, 20),
new CachedPower(0x813f3978f8940984L, 30, 28),
new CachedPower(0xc097ce7bc90715b3L, 56, 36),
new CachedPower(0x8f7e32ce7bea5c70L, 83, 44),
new CachedPower(0xd5d238a4abe98068L, 109, 52),
new CachedPower(0x9f4f2726179a2245L, 136, 60),
new CachedPower(0xed63a231d4c4fb27L, 162, 68),
new CachedPower(0xb0de65388cc8ada8L, 189, 76),
new CachedPower(0x83c7088e1aab65dbL, 216, 84),
new CachedPower(0xc45d1df942711d9aL, 242, 92),
new CachedPower(0x924d692ca61be758L, 269, 100),
new CachedPower(0xda01ee641a708deaL, 295, 108),
new CachedPower(0xa26da3999aef774aL, 322, 116),
new CachedPower(0xf209787bb47d6b85L, 348, 124),
new CachedPower(0xb454e4a179dd1877L, 375, 132),
new CachedPower(0x865b86925b9bc5c2L, 402, 140),
new CachedPower(0xc83553c5c8965d3dL, 428, 148),
new CachedPower(0x952ab45cfa97a0b3L, 455, 156),
new CachedPower(0xde469fbd99a05fe3L, 481, 164),
new CachedPower(0xa59bc234db398c25L, 508, 172),
new CachedPower(0xf6c69a72a3989f5cL, 534, 180),
new CachedPower(0xb7dcbf5354e9beceL, 561, 188),
new CachedPower(0x88fcf317f22241e2L, 588, 196),
new CachedPower(0xcc20ce9bd35c78a5L, 614, 204),
new CachedPower(0x98165af37b2153dfL, 641, 212),
new CachedPower(0xe2a0b5dc971f303aL, 667, 220),
new CachedPower(0xa8d9d1535ce3b396L, 694, 228),
new CachedPower(0xfb9b7cd9a4a7443cL, 720, 236),
new CachedPower(0xbb764c4ca7a44410L, 747, 244),
new CachedPower(0x8bab8eefb6409c1aL, 774, 252),
new CachedPower(0xd01fef10a657842cL, 800, 260),
new CachedPower(0x9b10a4e5e9913129L, 827, 268),
new CachedPower(0xe7109bfba19c0c9dL, 853, 276),
new CachedPower(0xac2820d9623bf429L, 880, 284),
new CachedPower(0x80444b5e7aa7cf85L, 907, 292),
new CachedPower(0xbf21e44003acdd2dL, 933, 300),
new CachedPower(0x8e679c2f5e44ff8fL, 960, 308),
new CachedPower(0xd433179d9c8cb841L, 986, 316),
new CachedPower(0x9e19db92b4e31ba9L, 1013, 324),
new CachedPower(0xeb96bf6ebadf77d9L, 1039, 332),
new CachedPower(0xaf87023b9bf0ee6bL, 1066, 340)
};
static final int kCachedPowersOffset = 348; // -1 * the first decimal_exponent.
static final double kD_1_LOG2_10 = 0.30102999566398114; // 1 / lg(10)
// Difference between the decimal exponents in the table above.
static final int kDecimalExponentDistance = 8;
static final int kMinDecimalExponent = -348;
static final int kMaxDecimalExponent = 340;
static int getCachedPowerForBinaryExponentRange(
final int min_exponent,
final int max_exponent,
final DiyFp power) {
final int kQ = DiyFp.kSignificandSize;
final double k = Math.ceil((min_exponent + kQ - 1) * kD_1_LOG2_10);
final int index =
(kCachedPowersOffset + (int) k - 1) / kDecimalExponentDistance + 1;
assert (0 <= index && index < kCachedPowers.length);
final CachedPower cached_power = kCachedPowers[index];
assert (min_exponent <= cached_power.binaryExponent);
assert (cached_power.binaryExponent <= max_exponent);
power.setF(cached_power.significand);
power.setE(cached_power.binaryExponent);
return cached_power.decimalExponent;
}
static int getCachedPowerForDecimalExponent(final int requested_exponent,
final DiyFp power) {
assert (kMinDecimalExponent <= requested_exponent);
assert (requested_exponent < kMaxDecimalExponent + kDecimalExponentDistance);
final int index =
(requested_exponent + kCachedPowersOffset) / kDecimalExponentDistance;
final CachedPower cached_power = kCachedPowers[index];
power.setF(cached_power.significand);
power.setE(cached_power.binaryExponent);
final int found_exponent = cached_power.decimalExponent;
assert (found_exponent <= requested_exponent);
assert (requested_exponent < found_exponent + kDecimalExponentDistance);
return cached_power.decimalExponent;
}
}

@ -0,0 +1,172 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jdk.nashorn.internal.runtime.doubleconv;
// This "Do It Yourself Floating Point" class implements a floating-point number
// with a uint64 significand and an int exponent. Normalized DiyFp numbers will
// have the most significant bit of the significand set.
// Multiplication and Subtraction do not normalize their results.
// DiyFp are not designed to contain special doubles (NaN and Infinity).
class DiyFp {
private long f_;
private int e_;
static final int kSignificandSize = 64;
static final long kUint64MSB = 0x8000000000000000L;
DiyFp() {
this.f_ = 0;
this.e_ = 0;
}
DiyFp(final long f, final int e) {
this.f_ = f;
this.e_ = e;
}
// this = this - other.
// The exponents of both numbers must be the same and the significand of this
// must be bigger than the significand of other.
// The result will not be normalized.
void subtract(final DiyFp other) {
assert (e_ == other.e_);
assert Long.compareUnsigned(f_, other.f_) >= 0;
f_ -= other.f_;
}
// Returns a - b.
// The exponents of both numbers must be the same and this must be bigger
// than other. The result will not be normalized.
static DiyFp minus(final DiyFp a, final DiyFp b) {
final DiyFp result = new DiyFp(a.f_, a.e_);
result.subtract(b);
return result;
}
// this = this * other.
final void multiply(final DiyFp other) {
// Simply "emulates" a 128 bit multiplication.
// However: the resulting number only contains 64 bits. The least
// significant 64 bits are only used for rounding the most significant 64
// bits.
final long kM32 = 0xFFFFFFFFL;
final long a = f_ >>> 32;
final long b = f_ & kM32;
final long c = other.f_ >>> 32;
final long d = other.f_ & kM32;
final long ac = a * c;
final long bc = b * c;
final long ad = a * d;
final long bd = b * d;
long tmp = (bd >>> 32) + (ad & kM32) + (bc & kM32);
// By adding 1U << 31 to tmp we round the final result.
// Halfway cases will be round up.
tmp += 1L << 31;
final long result_f = ac + (ad >>> 32) + (bc >>> 32) + (tmp >>> 32);
e_ += other.e_ + 64;
f_ = result_f;
}
// returns a * b;
static DiyFp times(final DiyFp a, final DiyFp b) {
final DiyFp result = new DiyFp(a.f_, a.e_);
result.multiply(b);
return result;
}
void normalize() {
assert(f_ != 0);
long significand = this.f_;
int exponent = this.e_;
// This method is mainly called for normalizing boundaries. In general
// boundaries need to be shifted by 10 bits. We thus optimize for this case.
final long k10MSBits = 0xFFC00000L << 32;
while ((significand & k10MSBits) == 0) {
significand <<= 10;
exponent -= 10;
}
while ((significand & kUint64MSB) == 0) {
significand <<= 1;
exponent--;
}
this.f_ = significand;
this.e_ = exponent;
}
static DiyFp normalize(final DiyFp a) {
final DiyFp result = new DiyFp(a.f_, a.e_);
result.normalize();
return result;
}
long f() { return f_; }
int e() { return e_; }
void setF(final long new_value) { f_ = new_value; }
void setE(final int new_value) { e_ = new_value; }
@Override
public String toString() {
return "DiyFp[f=" + f_ + ", e=" + e_ + "]";
}
}

@ -0,0 +1,214 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jdk.nashorn.internal.runtime.doubleconv;
/**
* This class provides the public API for the double conversion package.
*/
public final class DoubleConversion {
private final static int BUFFER_LENGTH = 30;
/**
* Converts a double number to its shortest string representation.
*
* @param value number to convert
* @return formatted number
*/
public static String toShortestString(final double value) {
final DtoaBuffer buffer = new DtoaBuffer(FastDtoa.kFastDtoaMaximalLength);
final double absValue = Math.abs(value);
if (value < 0) {
buffer.isNegative = true;
}
if (!fastDtoaShortest(absValue, buffer)) {
buffer.reset();
bignumDtoa(absValue, DtoaMode.SHORTEST, 0, buffer);
}
return buffer.format(DtoaMode.SHORTEST, 0);
}
/**
* Converts a double number to a string representation with a fixed number of digits
* after the decimal point.
*
* @param value number to convert.
* @param requestedDigits number of digits after decimal point
* @return formatted number
*/
public static String toFixed(final double value, final int requestedDigits) {
final DtoaBuffer buffer = new DtoaBuffer(BUFFER_LENGTH);
final double absValue = Math.abs(value);
if (value < 0) {
buffer.isNegative = true;
}
if (value == 0) {
buffer.append('0');
buffer.decimalPoint = 1;
} else if (!fixedDtoa(absValue, requestedDigits, buffer)) {
buffer.reset();
bignumDtoa(absValue, DtoaMode.FIXED, requestedDigits, buffer);
}
return buffer.format(DtoaMode.FIXED, requestedDigits);
}
/**
* Converts a double number to a string representation with a fixed number of digits.
*
* @param value number to convert
* @param precision number of digits to create
* @return formatted number
*/
public static String toPrecision(final double value, final int precision) {
final DtoaBuffer buffer = new DtoaBuffer(precision);
final double absValue = Math.abs(value);
if (value < 0) {
buffer.isNegative = true;
}
if (value == 0) {
for (int i = 0; i < precision; i++) {
buffer.append('0');
}
buffer.decimalPoint = 1;
} else if (!fastDtoaCounted(absValue, precision, buffer)) {
buffer.reset();
bignumDtoa(absValue, DtoaMode.PRECISION, precision, buffer);
}
return buffer.format(DtoaMode.PRECISION, 0);
}
/**
* Converts a double number to a string representation using the
* {@code BignumDtoa} algorithm and the specified conversion mode
* and number of digits.
*
* @param v number to convert
* @param mode conversion mode
* @param digits number of digits
* @param buffer buffer to use
*/
public static void bignumDtoa(final double v, final DtoaMode mode, final int digits, final DtoaBuffer buffer) {
assert(v > 0);
assert(!Double.isNaN(v));
assert(!Double.isInfinite(v));
BignumDtoa.bignumDtoa(v, mode, digits, buffer);
}
/**
* Converts a double number to its shortest string representation
* using the {@code FastDtoa} algorithm.
*
* @param v number to convert
* @param buffer buffer to use
* @return true if conversion succeeded
*/
public static boolean fastDtoaShortest(final double v, final DtoaBuffer buffer) {
assert(v > 0);
assert(!Double.isNaN(v));
assert(!Double.isInfinite(v));
return FastDtoa.grisu3(v, buffer);
}
/**
* Converts a double number to a string representation with the
* given number of digits using the {@code FastDtoa} algorithm.
*
* @param v number to convert
* @param precision number of digits to generate
* @param buffer buffer to use
* @return true if conversion succeeded
*/
public static boolean fastDtoaCounted(final double v, final int precision, final DtoaBuffer buffer) {
assert(v > 0);
assert(!Double.isNaN(v));
assert(!Double.isInfinite(v));
return FastDtoa.grisu3Counted(v, precision, buffer);
}
/**
* Converts a double number to a string representation with a
* fixed number of digits after the decimal point using the
* {@code FixedDtoa} algorithm.
*
* @param v number to convert.
* @param digits number of digits after the decimal point
* @param buffer buffer to use
* @return true if conversion succeeded
*/
public static boolean fixedDtoa(final double v, final int digits, final DtoaBuffer buffer) {
assert(v > 0);
assert(!Double.isNaN(v));
assert(!Double.isInfinite(v));
return FixedDtoa.fastFixedDtoa(v, digits, buffer);
}
}

@ -0,0 +1,223 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jdk.nashorn.internal.runtime.doubleconv;
/**
* A buffer for generating string representations of doubles.
*/
public class DtoaBuffer {
// The character buffer
final char[] chars;
// The number of characters in the buffer
int length = 0;
// The position of the decimal point
int decimalPoint = 0;
// Is this a negative number?
boolean isNegative = false;
/**
* Maximal length of numbers converted by FastDtoa
*/
public static final int kFastDtoaMaximalLength = FastDtoa.kFastDtoaMaximalLength;
/**
* Create a buffer with the given capacity.
* @param capacity the capacity of the buffer.
*/
public DtoaBuffer(final int capacity) {
chars = new char[capacity];
}
/**
* Append a character to the buffer, increasing its length.
* @param c character
*/
void append(final char c) {
chars[length++] = c;
}
/**
* Clear the buffer contents and set its length to {@code 0}.
*/
public void reset() {
length = 0;
decimalPoint = 0;
}
/**
* Get the raw digits of this buffer as string.
* @return the raw buffer contents
*/
public String getRawDigits() {
return new String(chars, 0, length);
}
/**
* Get the position of the decimal point.
* @return the decimal point position
*/
public int getDecimalPoint() {
return decimalPoint;
}
/**
* Returns the number of characters in the buffer.
* @return buffer length
*/
public int getLength() {
return length;
}
/**
* Returns the formatted buffer content as string, using the specified conversion mode
* and padding.
*
* @param mode conversion mode
* @param digitsAfterPoint number of digits after point
* @return formatted string
*/
public String format(final DtoaMode mode, final int digitsAfterPoint) {
final StringBuilder buffer = new StringBuilder();
if (isNegative) {
buffer.append('-');
}
// check for minus sign
switch (mode) {
case SHORTEST:
if (decimalPoint < -5 || decimalPoint > 21) {
toExponentialFormat(buffer);
} else {
toFixedFormat(buffer, digitsAfterPoint);
}
break;
case FIXED:
toFixedFormat(buffer, digitsAfterPoint);
break;
case PRECISION:
if (decimalPoint < -5 || decimalPoint > length) {
toExponentialFormat(buffer);
} else {
toFixedFormat(buffer, digitsAfterPoint);
}
break;
}
return buffer.toString();
}
private void toFixedFormat(final StringBuilder buffer, final int digitsAfterPoint) {
if (decimalPoint <= 0) {
// < 1,
buffer.append('0');
if (length > 0) {
buffer.append('.');
final int padding = -decimalPoint;
for (int i = 0; i < padding; i++) {
buffer.append('0');
}
buffer.append(chars, 0, length);
}
} else if (decimalPoint >= length) {
// large integer, add trailing zeroes
buffer.append(chars, 0, length);
for (int i = length; i < decimalPoint; i++) {
buffer.append('0');
}
} else if (decimalPoint < length) {
// >= 1, split decimals and insert decimalPoint
buffer.append(chars, 0, decimalPoint);
buffer.append('.');
buffer.append(chars, decimalPoint, length - decimalPoint);
}
// Create trailing zeros if requested
if (digitsAfterPoint > 0) {
if (decimalPoint >= length) {
buffer.append('.');
}
for (int i = Math.max(0, length - decimalPoint); i < digitsAfterPoint; i++) {
buffer.append('0');
}
}
}
private void toExponentialFormat(final StringBuilder buffer) {
buffer.append(chars[0]);
if (length > 1) {
// insert decimal decimalPoint if more than one digit was produced
buffer.append('.');
buffer.append(chars, 1, length - 1);
}
buffer.append('e');
final int exponent = decimalPoint - 1;
if (exponent > 0) {
buffer.append('+');
}
buffer.append(exponent);
}
@Override
public String toString() {
return "[chars:" + new String(chars, 0, length) + ", decimalPoint:" + decimalPoint + "]";
}
}

@ -0,0 +1,82 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jdk.nashorn.internal.runtime.doubleconv;
/**
* This defines the string conversion modes supported by this package.
* The original C++ library also supports SHORTEST-SINGLE for single
* precision floats but we don't since we always operate with doubles.
*/
public enum DtoaMode {
/**
* Produce the shortest correct representation.
* For example the output of 0.299999999999999988897 is (the less accurate
* but correct) 0.3.
*/
SHORTEST,
/**
* Produce a fixed number of digits after the decimal point.
* For instance fixed(0.1, 4) becomes 0.1000
* If the input number is big, the output will be big.
*/
FIXED,
/**
* Fixed number of digits (independent of the decimal point).
*/
PRECISION
}

@ -0,0 +1,651 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jdk.nashorn.internal.runtime.doubleconv;
// Fast Dtoa implementation supporting shortest and precision modes. Does not
// work for all numbers so BugnumDtoa is used as fallback.
class FastDtoa {
// FastDtoa will produce at most kFastDtoaMaximalLength digits. This does not
// include the terminating '\0' character.
static final int kFastDtoaMaximalLength = 17;
// The minimal and maximal target exponent define the range of w's binary
// exponent, where 'w' is the result of multiplying the input by a cached power
// of ten.
//
// A different range might be chosen on a different platform, to optimize digit
// generation, but a smaller range requires more powers of ten to be cached.
static final int kMinimalTargetExponent = -60;
static final int kMaximalTargetExponent = -32;
// Adjusts the last digit of the generated number, and screens out generated
// solutions that may be inaccurate. A solution may be inaccurate if it is
// outside the safe interval, or if we cannot prove that it is closer to the
// input than a neighboring representation of the same length.
//
// Input: * buffer containing the digits of too_high / 10^kappa
// * distance_too_high_w == (too_high - w).f() * unit
// * unsafe_interval == (too_high - too_low).f() * unit
// * rest = (too_high - buffer * 10^kappa).f() * unit
// * ten_kappa = 10^kappa * unit
// * unit = the common multiplier
// Output: returns true if the buffer is guaranteed to contain the closest
// representable number to the input.
// Modifies the generated digits in the buffer to approach (round towards) w.
static boolean roundWeed(final DtoaBuffer buffer,
final long distance_too_high_w,
final long unsafe_interval,
long rest,
final long ten_kappa,
final long unit) {
final long small_distance = distance_too_high_w - unit;
final long big_distance = distance_too_high_w + unit;
// Let w_low = too_high - big_distance, and
// w_high = too_high - small_distance.
// Note: w_low < w < w_high
//
// The real w (* unit) must lie somewhere inside the interval
// ]w_low; w_high[ (often written as "(w_low; w_high)")
// Basically the buffer currently contains a number in the unsafe interval
// ]too_low; too_high[ with too_low < w < too_high
//
// too_high - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// ^v 1 unit ^ ^ ^ ^
// boundary_high --------------------- . . . .
// ^v 1 unit . . . .
// - - - - - - - - - - - - - - - - - - - + - - + - - - - - - . .
// . . ^ . .
// . big_distance . . .
// . . . . rest
// small_distance . . . .
// v . . . .
// w_high - - - - - - - - - - - - - - - - - - . . . .
// ^v 1 unit . . . .
// w ---------------------------------------- . . . .
// ^v 1 unit v . . .
// w_low - - - - - - - - - - - - - - - - - - - - - . . .
// . . v
// buffer --------------------------------------------------+-------+--------
// . .
// safe_interval .
// v .
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .
// ^v 1 unit .
// boundary_low ------------------------- unsafe_interval
// ^v 1 unit v
// too_low - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//
//
// Note that the value of buffer could lie anywhere inside the range too_low
// to too_high.
//
// boundary_low, boundary_high and w are approximations of the real boundaries
// and v (the input number). They are guaranteed to be precise up to one unit.
// In fact the error is guaranteed to be strictly less than one unit.
//
// Anything that lies outside the unsafe interval is guaranteed not to round
// to v when read again.
// Anything that lies inside the safe interval is guaranteed to round to v
// when read again.
// If the number inside the buffer lies inside the unsafe interval but not
// inside the safe interval then we simply do not know and bail out (returning
// false).
//
// Similarly we have to take into account the imprecision of 'w' when finding
// the closest representation of 'w'. If we have two potential
// representations, and one is closer to both w_low and w_high, then we know
// it is closer to the actual value v.
//
// By generating the digits of too_high we got the largest (closest to
// too_high) buffer that is still in the unsafe interval. In the case where
// w_high < buffer < too_high we try to decrement the buffer.
// This way the buffer approaches (rounds towards) w.
// There are 3 conditions that stop the decrementation process:
// 1) the buffer is already below w_high
// 2) decrementing the buffer would make it leave the unsafe interval
// 3) decrementing the buffer would yield a number below w_high and farther
// away than the current number. In other words:
// (buffer{-1} < w_high) && w_high - buffer{-1} > buffer - w_high
// Instead of using the buffer directly we use its distance to too_high.
// Conceptually rest ~= too_high - buffer
// We need to do the following tests in this order to avoid over- and
// underflows.
assert (Long.compareUnsigned(rest, unsafe_interval) <= 0);
while (Long.compareUnsigned(rest, small_distance) < 0 && // Negated condition 1
Long.compareUnsigned(unsafe_interval - rest, ten_kappa) >= 0 && // Negated condition 2
(Long.compareUnsigned(rest + ten_kappa, small_distance) < 0 || // buffer{-1} > w_high
Long.compareUnsigned(small_distance - rest, rest + ten_kappa - small_distance) >= 0)) {
buffer.chars[buffer.length - 1]--;
rest += ten_kappa;
}
// We have approached w+ as much as possible. We now test if approaching w-
// would require changing the buffer. If yes, then we have two possible
// representations close to w, but we cannot decide which one is closer.
if (Long.compareUnsigned(rest, big_distance) < 0 &&
Long.compareUnsigned(unsafe_interval - rest, ten_kappa) >= 0 &&
(Long.compareUnsigned(rest + ten_kappa, big_distance) < 0 ||
Long.compareUnsigned(big_distance - rest, rest + ten_kappa - big_distance) > 0)) {
return false;
}
// Weeding test.
// The safe interval is [too_low + 2 ulp; too_high - 2 ulp]
// Since too_low = too_high - unsafe_interval this is equivalent to
// [too_high - unsafe_interval + 4 ulp; too_high - 2 ulp]
// Conceptually we have: rest ~= too_high - buffer
return Long.compareUnsigned(2 * unit, rest) <= 0 && Long.compareUnsigned(rest, unsafe_interval - 4 * unit) <= 0;
}
// Rounds the buffer upwards if the result is closer to v by possibly adding
// 1 to the buffer. If the precision of the calculation is not sufficient to
// round correctly, return false.
// The rounding might shift the whole buffer in which case the kappa is
// adjusted. For example "99", kappa = 3 might become "10", kappa = 4.
//
// If 2*rest > ten_kappa then the buffer needs to be round up.
// rest can have an error of +/- 1 unit. This function accounts for the
// imprecision and returns false, if the rounding direction cannot be
// unambiguously determined.
//
// Precondition: rest < ten_kappa.
// Changed return type to int to let caller know they should increase kappa (return value 2)
static int roundWeedCounted(final char[] buffer,
final int length,
final long rest,
final long ten_kappa,
final long unit) {
assert(Long.compareUnsigned(rest, ten_kappa) < 0);
// The following tests are done in a specific order to avoid overflows. They
// will work correctly with any uint64 values of rest < ten_kappa and unit.
//
// If the unit is too big, then we don't know which way to round. For example
// a unit of 50 means that the real number lies within rest +/- 50. If
// 10^kappa == 40 then there is no way to tell which way to round.
if (Long.compareUnsigned(unit, ten_kappa) >= 0) return 0;
// Even if unit is just half the size of 10^kappa we are already completely
// lost. (And after the previous test we know that the expression will not
// over/underflow.)
if (Long.compareUnsigned(ten_kappa - unit, unit) <= 0) return 0;
// If 2 * (rest + unit) <= 10^kappa we can safely round down.
if (Long.compareUnsigned(ten_kappa - rest, rest) > 0 && Long.compareUnsigned(ten_kappa - 2 * rest, 2 * unit) >= 0) {
return 1;
}
// If 2 * (rest - unit) >= 10^kappa, then we can safely round up.
if (Long.compareUnsigned(rest, unit) > 0 && Long.compareUnsigned(ten_kappa - (rest - unit), (rest - unit)) <= 0) {
// Increment the last digit recursively until we find a non '9' digit.
buffer[length - 1]++;
for (int i = length - 1; i > 0; --i) {
if (buffer[i] != '0' + 10) break;
buffer[i] = '0';
buffer[i - 1]++;
}
// If the first digit is now '0'+ 10 we had a buffer with all '9's. With the
// exception of the first digit all digits are now '0'. Simply switch the
// first digit to '1' and adjust the kappa. Example: "99" becomes "10" and
// the power (the kappa) is increased.
if (buffer[0] == '0' + 10) {
buffer[0] = '1';
// Return value of 2 tells caller to increase (*kappa) += 1
return 2;
}
return 1;
}
return 0;
}
// Returns the biggest power of ten that is less than or equal to the given
// number. We furthermore receive the maximum number of bits 'number' has.
//
// Returns power == 10^(exponent_plus_one-1) such that
// power <= number < power * 10.
// If number_bits == 0 then 0^(0-1) is returned.
// The number of bits must be <= 32.
// Precondition: number < (1 << (number_bits + 1)).
// Inspired by the method for finding an integer log base 10 from here:
// http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
static final int kSmallPowersOfTen[] =
{0, 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000,
1000000000};
// Returns the biggest power of ten that is less than or equal than the given
// number. We furthermore receive the maximum number of bits 'number' has.
// If number_bits == 0 then 0^-1 is returned
// The number of bits must be <= 32.
// Precondition: (1 << number_bits) <= number < (1 << (number_bits + 1)).
static long biggestPowerTen(final int number,
final int number_bits) {
final int power, exponent_plus_one;
assert ((number & 0xFFFFFFFFL) < (1l << (number_bits + 1)));
// 1233/4096 is approximately 1/lg(10).
int exponent_plus_one_guess = ((number_bits + 1) * 1233 >>> 12);
// We increment to skip over the first entry in the kPowersOf10 table.
// Note: kPowersOf10[i] == 10^(i-1).
exponent_plus_one_guess++;
// We don't have any guarantees that 2^number_bits <= number.
if (number < kSmallPowersOfTen[exponent_plus_one_guess]) {
exponent_plus_one_guess--;
}
power = kSmallPowersOfTen[exponent_plus_one_guess];
exponent_plus_one = exponent_plus_one_guess;
return ((long) power << 32) | (long) exponent_plus_one;
}
// Generates the digits of input number w.
// w is a floating-point number (DiyFp), consisting of a significand and an
// exponent. Its exponent is bounded by kMinimalTargetExponent and
// kMaximalTargetExponent.
// Hence -60 <= w.e() <= -32.
//
// Returns false if it fails, in which case the generated digits in the buffer
// should not be used.
// Preconditions:
// * low, w and high are correct up to 1 ulp (unit in the last place). That
// is, their error must be less than a unit of their last digits.
// * low.e() == w.e() == high.e()
// * low < w < high, and taking into account their error: low~ <= high~
// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
// Postconditions: returns false if procedure fails.
// otherwise:
// * buffer is not null-terminated, but len contains the number of digits.
// * buffer contains the shortest possible decimal digit-sequence
// such that LOW < buffer * 10^kappa < HIGH, where LOW and HIGH are the
// correct values of low and high (without their error).
// * if more than one decimal representation gives the minimal number of
// decimal digits then the one closest to W (where W is the correct value
// of w) is chosen.
// Remark: this procedure takes into account the imprecision of its input
// numbers. If the precision is not enough to guarantee all the postconditions
// then false is returned. This usually happens rarely (~0.5%).
//
// Say, for the sake of example, that
// w.e() == -48, and w.f() == 0x1234567890abcdef
// w's value can be computed by w.f() * 2^w.e()
// We can obtain w's integral digits by simply shifting w.f() by -w.e().
// -> w's integral part is 0x1234
// w's fractional part is therefore 0x567890abcdef.
// Printing w's integral part is easy (simply print 0x1234 in decimal).
// In order to print its fraction we repeatedly multiply the fraction by 10 and
// get each digit. Example the first digit after the point would be computed by
// (0x567890abcdef * 10) >> 48. -> 3
// The whole thing becomes slightly more complicated because we want to stop
// once we have enough digits. That is, once the digits inside the buffer
// represent 'w' we can stop. Everything inside the interval low - high
// represents w. However we have to pay attention to low, high and w's
// imprecision.
static boolean digitGen(final DiyFp low,
final DiyFp w,
final DiyFp high,
final DtoaBuffer buffer,
final int mk) {
assert(low.e() == w.e() && w.e() == high.e());
assert Long.compareUnsigned(low.f() + 1, high.f() - 1) <= 0;
assert(kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent);
// low, w and high are imprecise, but by less than one ulp (unit in the last
// place).
// If we remove (resp. add) 1 ulp from low (resp. high) we are certain that
// the new numbers are outside of the interval we want the final
// representation to lie in.
// Inversely adding (resp. removing) 1 ulp from low (resp. high) would yield
// numbers that are certain to lie in the interval. We will use this fact
// later on.
// We will now start by generating the digits within the uncertain
// interval. Later we will weed out representations that lie outside the safe
// interval and thus _might_ lie outside the correct interval.
long unit = 1;
final DiyFp too_low = new DiyFp(low.f() - unit, low.e());
final DiyFp too_high = new DiyFp(high.f() + unit, high.e());
// too_low and too_high are guaranteed to lie outside the interval we want the
// generated number in.
final DiyFp unsafe_interval = DiyFp.minus(too_high, too_low);
// We now cut the input number into two parts: the integral digits and the
// fractionals. We will not write any decimal separator though, but adapt
// kappa instead.
// Reminder: we are currently computing the digits (stored inside the buffer)
// such that: too_low < buffer * 10^kappa < too_high
// We use too_high for the digit_generation and stop as soon as possible.
// If we stop early we effectively round down.
final DiyFp one = new DiyFp(1l << -w.e(), w.e());
// Division by one is a shift.
int integrals = (int)(too_high.f() >>> -one.e());
// Modulo by one is an and.
long fractionals = too_high.f() & (one.f() - 1);
int divisor;
final int divisor_exponent_plus_one;
final long result = biggestPowerTen(integrals, DiyFp.kSignificandSize - (-one.e()));
divisor = (int) (result >>> 32);
divisor_exponent_plus_one = (int) result;
int kappa = divisor_exponent_plus_one;
// Loop invariant: buffer = too_high / 10^kappa (integer division)
// The invariant holds for the first iteration: kappa has been initialized
// with the divisor exponent + 1. And the divisor is the biggest power of ten
// that is smaller than integrals.
while (kappa > 0) {
final int digit = integrals / divisor;
assert (digit <= 9);
buffer.append((char) ('0' + digit));
integrals %= divisor;
kappa--;
// Note that kappa now equals the exponent of the divisor and that the
// invariant thus holds again.
final long rest =
((long) integrals << -one.e()) + fractionals;
// Invariant: too_high = buffer * 10^kappa + DiyFp(rest, one.e())
// Reminder: unsafe_interval.e() == one.e()
if (Long.compareUnsigned(rest, unsafe_interval.f()) < 0) {
// Rounding down (by not emitting the remaining digits) yields a number
// that lies within the unsafe interval.
buffer.decimalPoint = buffer.length - mk + kappa;
return roundWeed(buffer, DiyFp.minus(too_high, w).f(),
unsafe_interval.f(), rest,
(long) divisor << -one.e(), unit);
}
divisor /= 10;
}
// The integrals have been generated. We are at the point of the decimal
// separator. In the following loop we simply multiply the remaining digits by
// 10 and divide by one. We just need to pay attention to multiply associated
// data (like the interval or 'unit'), too.
// Note that the multiplication by 10 does not overflow, because w.e >= -60
// and thus one.e >= -60.
assert (one.e() >= -60);
assert (fractionals < one.f());
assert (Long.compareUnsigned(Long.divideUnsigned(0xFFFFFFFFFFFFFFFFL, 10), one.f()) >= 0);
for (;;) {
fractionals *= 10;
unit *= 10;
unsafe_interval.setF(unsafe_interval.f() * 10);
// Integer division by one.
final int digit = (int) (fractionals >>> -one.e());
assert (digit <= 9);
buffer.append((char) ('0' + digit));
fractionals &= one.f() - 1; // Modulo by one.
kappa--;
if (Long.compareUnsigned(fractionals, unsafe_interval.f()) < 0) {
buffer.decimalPoint = buffer.length - mk + kappa;
return roundWeed(buffer, DiyFp.minus(too_high, w).f() * unit,
unsafe_interval.f(), fractionals, one.f(), unit);
}
}
}
// Generates (at most) requested_digits digits of input number w.
// w is a floating-point number (DiyFp), consisting of a significand and an
// exponent. Its exponent is bounded by kMinimalTargetExponent and
// kMaximalTargetExponent.
// Hence -60 <= w.e() <= -32.
//
// Returns false if it fails, in which case the generated digits in the buffer
// should not be used.
// Preconditions:
// * w is correct up to 1 ulp (unit in the last place). That
// is, its error must be strictly less than a unit of its last digit.
// * kMinimalTargetExponent <= w.e() <= kMaximalTargetExponent
//
// Postconditions: returns false if procedure fails.
// otherwise:
// * buffer is not null-terminated, but length contains the number of
// digits.
// * the representation in buffer is the most precise representation of
// requested_digits digits.
// * buffer contains at most requested_digits digits of w. If there are less
// than requested_digits digits then some trailing '0's have been removed.
// * kappa is such that
// w = buffer * 10^kappa + eps with |eps| < 10^kappa / 2.
//
// Remark: This procedure takes into account the imprecision of its input
// numbers. If the precision is not enough to guarantee all the postconditions
// then false is returned. This usually happens rarely, but the failure-rate
// increases with higher requested_digits.
static boolean digitGenCounted(final DiyFp w,
int requested_digits,
final DtoaBuffer buffer,
final int mk) {
assert (kMinimalTargetExponent <= w.e() && w.e() <= kMaximalTargetExponent);
assert (kMinimalTargetExponent >= -60);
assert (kMaximalTargetExponent <= -32);
// w is assumed to have an error less than 1 unit. Whenever w is scaled we
// also scale its error.
long w_error = 1;
// We cut the input number into two parts: the integral digits and the
// fractional digits. We don't emit any decimal separator, but adapt kappa
// instead. Example: instead of writing "1.2" we put "12" into the buffer and
// increase kappa by 1.
final DiyFp one = new DiyFp(1l << -w.e(), w.e());
// Division by one is a shift.
int integrals = (int) (w.f() >>> -one.e());
// Modulo by one is an and.
long fractionals = w.f() & (one.f() - 1);
int divisor;
final int divisor_exponent_plus_one;
final long biggestPower = biggestPowerTen(integrals, DiyFp.kSignificandSize - (-one.e()));
divisor = (int) (biggestPower >>> 32);
divisor_exponent_plus_one = (int) biggestPower;
int kappa = divisor_exponent_plus_one;
// Loop invariant: buffer = w / 10^kappa (integer division)
// The invariant holds for the first iteration: kappa has been initialized
// with the divisor exponent + 1. And the divisor is the biggest power of ten
// that is smaller than 'integrals'.
while (kappa > 0) {
final int digit = integrals / divisor;
assert (digit <= 9);
buffer.append((char) ('0' + digit));
requested_digits--;
integrals %= divisor;
kappa--;
// Note that kappa now equals the exponent of the divisor and that the
// invariant thus holds again.
if (requested_digits == 0) break;
divisor /= 10;
}
if (requested_digits == 0) {
final long rest =
((long) (integrals) << -one.e()) + fractionals;
final int result = roundWeedCounted(buffer.chars, buffer.length, rest,
(long) divisor << -one.e(), w_error);
buffer.decimalPoint = buffer.length - mk + kappa + (result == 2 ? 1 : 0);
return result > 0;
}
// The integrals have been generated. We are at the decimalPoint of the decimal
// separator. In the following loop we simply multiply the remaining digits by
// 10 and divide by one. We just need to pay attention to multiply associated
// data (the 'unit'), too.
// Note that the multiplication by 10 does not overflow, because w.e >= -60
// and thus one.e >= -60.
assert (one.e() >= -60);
assert (fractionals < one.f());
assert (Long.compareUnsigned(Long.divideUnsigned(0xFFFFFFFFFFFFFFFFL, 10), one.f()) >= 0);
while (requested_digits > 0 && fractionals > w_error) {
fractionals *= 10;
w_error *= 10;
// Integer division by one.
final int digit = (int) (fractionals >>> -one.e());
assert (digit <= 9);
buffer.append((char) ('0' + digit));
requested_digits--;
fractionals &= one.f() - 1; // Modulo by one.
kappa--;
}
if (requested_digits != 0) return false;
final int result = roundWeedCounted(buffer.chars, buffer.length, fractionals, one.f(), w_error);
buffer.decimalPoint = buffer.length - mk + kappa + (result == 2 ? 1 : 0);
return result > 0;
}
// Provides a decimal representation of v.
// Returns true if it succeeds, otherwise the result cannot be trusted.
// There will be *length digits inside the buffer (not null-terminated).
// If the function returns true then
// v == (double) (buffer * 10^decimal_exponent).
// The digits in the buffer are the shortest representation possible: no
// 0.09999999999999999 instead of 0.1. The shorter representation will even be
// chosen even if the longer one would be closer to v.
// The last digit will be closest to the actual v. That is, even if several
// digits might correctly yield 'v' when read again, the closest will be
// computed.
static boolean grisu3(final double v, final DtoaBuffer buffer) {
final long d64 = IeeeDouble.doubleToLong(v);
final DiyFp w = IeeeDouble.asNormalizedDiyFp(d64);
// boundary_minus and boundary_plus are the boundaries between v and its
// closest floating-point neighbors. Any number strictly between
// boundary_minus and boundary_plus will round to v when convert to a double.
// Grisu3 will never output representations that lie exactly on a boundary.
final DiyFp boundary_minus = new DiyFp(), boundary_plus = new DiyFp();
IeeeDouble.normalizedBoundaries(d64, boundary_minus, boundary_plus);
assert(boundary_plus.e() == w.e());
final DiyFp ten_mk = new DiyFp(); // Cached power of ten: 10^-k
final int mk; // -k
final int ten_mk_minimal_binary_exponent =
kMinimalTargetExponent - (w.e() + DiyFp.kSignificandSize);
final int ten_mk_maximal_binary_exponent =
kMaximalTargetExponent - (w.e() + DiyFp.kSignificandSize);
mk = CachedPowers.getCachedPowerForBinaryExponentRange(
ten_mk_minimal_binary_exponent,
ten_mk_maximal_binary_exponent,
ten_mk);
assert(kMinimalTargetExponent <= w.e() + ten_mk.e() +
DiyFp.kSignificandSize &&
kMaximalTargetExponent >= w.e() + ten_mk.e() +
DiyFp.kSignificandSize);
// Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
// 64 bit significand and ten_mk is thus only precise up to 64 bits.
// The DiyFp::Times procedure rounds its result, and ten_mk is approximated
// too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
// off by a small amount.
// In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
// In other words: let f = scaled_w.f() and e = scaled_w.e(), then
// (f-1) * 2^e < w*10^k < (f+1) * 2^e
final DiyFp scaled_w = DiyFp.times(w, ten_mk);
assert(scaled_w.e() ==
boundary_plus.e() + ten_mk.e() + DiyFp.kSignificandSize);
// In theory it would be possible to avoid some recomputations by computing
// the difference between w and boundary_minus/plus (a power of 2) and to
// compute scaled_boundary_minus/plus by subtracting/adding from
// scaled_w. However the code becomes much less readable and the speed
// enhancements are not terriffic.
final DiyFp scaled_boundary_minus = DiyFp.times(boundary_minus, ten_mk);
final DiyFp scaled_boundary_plus = DiyFp.times(boundary_plus, ten_mk);
// DigitGen will generate the digits of scaled_w. Therefore we have
// v == (double) (scaled_w * 10^-mk).
// Set decimal_exponent == -mk and pass it to DigitGen. If scaled_w is not an
// integer than it will be updated. For instance if scaled_w == 1.23 then
// the buffer will be filled with "123" und the decimal_exponent will be
// decreased by 2.
final boolean result = digitGen(scaled_boundary_minus, scaled_w, scaled_boundary_plus,
buffer, mk);
return result;
}
// The "counted" version of grisu3 (see above) only generates requested_digits
// number of digits. This version does not generate the shortest representation,
// and with enough requested digits 0.1 will at some point print as 0.9999999...
// Grisu3 is too imprecise for real halfway cases (1.5 will not work) and
// therefore the rounding strategy for halfway cases is irrelevant.
static boolean grisu3Counted(final double v,
final int requested_digits,
final DtoaBuffer buffer) {
final long d64 = IeeeDouble.doubleToLong(v);
final DiyFp w = IeeeDouble.asNormalizedDiyFp(d64);
final DiyFp ten_mk = new DiyFp(); // Cached power of ten: 10^-k
final int mk; // -k
final int ten_mk_minimal_binary_exponent =
kMinimalTargetExponent - (w.e() + DiyFp.kSignificandSize);
final int ten_mk_maximal_binary_exponent =
kMaximalTargetExponent - (w.e() + DiyFp.kSignificandSize);
mk = CachedPowers.getCachedPowerForBinaryExponentRange(
ten_mk_minimal_binary_exponent,
ten_mk_maximal_binary_exponent,
ten_mk);
assert ((kMinimalTargetExponent <= w.e() + ten_mk.e() +
DiyFp.kSignificandSize) &&
(kMaximalTargetExponent >= w.e() + ten_mk.e() +
DiyFp.kSignificandSize));
// Note that ten_mk is only an approximation of 10^-k. A DiyFp only contains a
// 64 bit significand and ten_mk is thus only precise up to 64 bits.
// The DiyFp::Times procedure rounds its result, and ten_mk is approximated
// too. The variable scaled_w (as well as scaled_boundary_minus/plus) are now
// off by a small amount.
// In fact: scaled_w - w*10^k < 1ulp (unit in the last place) of scaled_w.
// In other words: let f = scaled_w.f() and e = scaled_w.e(), then
// (f-1) * 2^e < w*10^k < (f+1) * 2^e
final DiyFp scaled_w = DiyFp.times(w, ten_mk);
// We now have (double) (scaled_w * 10^-mk).
// DigitGen will generate the first requested_digits digits of scaled_w and
// return together with a kappa such that scaled_w ~= buffer * 10^kappa. (It
// will not always be exactly the same since DigitGenCounted only produces a
// limited number of digits.)
final boolean result = digitGenCounted(scaled_w, requested_digits,
buffer, mk);
return result;
}
}

@ -0,0 +1,425 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jdk.nashorn.internal.runtime.doubleconv;
class FixedDtoa {
// Represents a 128bit type. This class should be replaced by a native type on
// platforms that support 128bit integers.
static class UInt128 {
private static final long kMask32 = 0xFFFFFFFFL;
// Value == (high_bits_ << 64) + low_bits_
private long high_bits_;
private long low_bits_;
UInt128(final long high_bits, final long low_bits) {
this.high_bits_ = high_bits;
this.low_bits_ = low_bits;
}
void multiply(final int multiplicand) {
long accumulator;
accumulator = (low_bits_ & kMask32) * multiplicand;
long part = accumulator & kMask32;
accumulator >>>= 32;
accumulator = accumulator + (low_bits_ >>> 32) * multiplicand;
low_bits_ = (accumulator << 32) + part;
accumulator >>>= 32;
accumulator = accumulator + (high_bits_ & kMask32) * multiplicand;
part = accumulator & kMask32;
accumulator >>>= 32;
accumulator = accumulator + (high_bits_ >>> 32) * multiplicand;
high_bits_ = (accumulator << 32) + part;
assert ((accumulator >>> 32) == 0);
}
void shift(final int shift_amount) {
assert (-64 <= shift_amount && shift_amount <= 64);
if (shift_amount == 0) {
return;
} else if (shift_amount == -64) {
high_bits_ = low_bits_;
low_bits_ = 0;
} else if (shift_amount == 64) {
low_bits_ = high_bits_;
high_bits_ = 0;
} else if (shift_amount <= 0) {
high_bits_ <<= -shift_amount;
high_bits_ += low_bits_ >>> (64 + shift_amount);
low_bits_ <<= -shift_amount;
} else {
low_bits_ >>>= shift_amount;
low_bits_ += high_bits_ << (64 - shift_amount);
high_bits_ >>>= shift_amount;
}
}
// Modifies *this to *this MOD (2^power).
// Returns *this DIV (2^power).
int divModPowerOf2(final int power) {
if (power >= 64) {
final int result = (int) (high_bits_ >>> (power - 64));
high_bits_ -= (long) (result) << (power - 64);
return result;
} else {
final long part_low = low_bits_ >>> power;
final long part_high = high_bits_ << (64 - power);
final int result = (int) (part_low + part_high);
high_bits_ = 0;
low_bits_ -= part_low << power;
return result;
}
}
boolean isZero() {
return high_bits_ == 0 && low_bits_ == 0;
}
int bitAt(final int position) {
if (position >= 64) {
return (int) (high_bits_ >>> (position - 64)) & 1;
} else {
return (int) (low_bits_ >>> position) & 1;
}
}
};
static final int kDoubleSignificandSize = 53; // Includes the hidden bit.
static void fillDigits32FixedLength(int number, final int requested_length,
final DtoaBuffer buffer) {
for (int i = requested_length - 1; i >= 0; --i) {
buffer.chars[buffer.length + i] = (char) ('0' + Integer.remainderUnsigned(number, 10));
number = Integer.divideUnsigned(number, 10);
}
buffer.length += requested_length;
}
static void fillDigits32(int number, final DtoaBuffer buffer) {
int number_length = 0;
// We fill the digits in reverse order and exchange them afterwards.
while (number != 0) {
final int digit = Integer.remainderUnsigned(number, 10);
number = Integer.divideUnsigned(number, 10);
buffer.chars[buffer.length + number_length] = (char) ('0' + digit);
number_length++;
}
// Exchange the digits.
int i = buffer.length;
int j = buffer.length + number_length - 1;
while (i < j) {
final char tmp = buffer.chars[i];
buffer.chars[i] = buffer.chars[j];
buffer.chars[j] = tmp;
i++;
j--;
}
buffer.length += number_length;
}
static void fillDigits64FixedLength(long number, final DtoaBuffer buffer) {
final int kTen7 = 10000000;
// For efficiency cut the number into 3 uint32_t parts, and print those.
final int part2 = (int) Long.remainderUnsigned(number, kTen7);
number = Long.divideUnsigned(number, kTen7);
final int part1 = (int) Long.remainderUnsigned(number, kTen7);
final int part0 = (int) Long.divideUnsigned(number, kTen7);
fillDigits32FixedLength(part0, 3, buffer);
fillDigits32FixedLength(part1, 7, buffer);
fillDigits32FixedLength(part2, 7, buffer);
}
static void FillDigits64(long number, final DtoaBuffer buffer) {
final int kTen7 = 10000000;
// For efficiency cut the number into 3 uint32_t parts, and print those.
final int part2 = (int) Long.remainderUnsigned(number, kTen7);
number = Long.divideUnsigned(number, kTen7);
final int part1 = (int) Long.remainderUnsigned(number, kTen7);
final int part0 = (int) Long.divideUnsigned(number, kTen7);
if (part0 != 0) {
fillDigits32(part0, buffer);
fillDigits32FixedLength(part1, 7, buffer);
fillDigits32FixedLength(part2, 7, buffer);
} else if (part1 != 0) {
fillDigits32(part1, buffer);
fillDigits32FixedLength(part2, 7, buffer);
} else {
fillDigits32(part2, buffer);
}
}
static void roundUp(final DtoaBuffer buffer) {
// An empty buffer represents 0.
if (buffer.length == 0) {
buffer.chars[0] = '1';
buffer.decimalPoint = 1;
buffer.length = 1;
return;
}
// Round the last digit until we either have a digit that was not '9' or until
// we reached the first digit.
buffer.chars[buffer.length - 1]++;
for (int i = buffer.length - 1; i > 0; --i) {
if (buffer.chars[i] != '0' + 10) {
return;
}
buffer.chars[i] = '0';
buffer.chars[i - 1]++;
}
// If the first digit is now '0' + 10, we would need to set it to '0' and add
// a '1' in front. However we reach the first digit only if all following
// digits had been '9' before rounding up. Now all trailing digits are '0' and
// we simply switch the first digit to '1' and update the decimal-point
// (indicating that the point is now one digit to the right).
if (buffer.chars[0] == '0' + 10) {
buffer.chars[0] = '1';
buffer.decimalPoint++;
}
}
// The given fractionals number represents a fixed-point number with binary
// point at bit (-exponent).
// Preconditions:
// -128 <= exponent <= 0.
// 0 <= fractionals * 2^exponent < 1
// The buffer holds the result.
// The function will round its result. During the rounding-process digits not
// generated by this function might be updated, and the decimal-point variable
// might be updated. If this function generates the digits 99 and the buffer
// already contained "199" (thus yielding a buffer of "19999") then a
// rounding-up will change the contents of the buffer to "20000".
static void fillFractionals(long fractionals, final int exponent,
final int fractional_count, final DtoaBuffer buffer) {
assert (-128 <= exponent && exponent <= 0);
// 'fractionals' is a fixed-decimalPoint number, with binary decimalPoint at bit
// (-exponent). Inside the function the non-converted remainder of fractionals
// is a fixed-decimalPoint number, with binary decimalPoint at bit 'decimalPoint'.
if (-exponent <= 64) {
// One 64 bit number is sufficient.
assert (fractionals >>> 56 == 0);
int point = -exponent;
for (int i = 0; i < fractional_count; ++i) {
if (fractionals == 0) break;
// Instead of multiplying by 10 we multiply by 5 and adjust the point
// location. This way the fractionals variable will not overflow.
// Invariant at the beginning of the loop: fractionals < 2^point.
// Initially we have: point <= 64 and fractionals < 2^56
// After each iteration the point is decremented by one.
// Note that 5^3 = 125 < 128 = 2^7.
// Therefore three iterations of this loop will not overflow fractionals
// (even without the subtraction at the end of the loop body). At this
// time point will satisfy point <= 61 and therefore fractionals < 2^point
// and any further multiplication of fractionals by 5 will not overflow.
fractionals *= 5;
point--;
final int digit = (int) (fractionals >>> point);
assert (digit <= 9);
buffer.chars[buffer.length] = (char) ('0' + digit);
buffer.length++;
fractionals -= (long) (digit) << point;
}
// If the first bit after the point is set we have to round up.
if (((fractionals >>> (point - 1)) & 1) == 1) {
roundUp(buffer);
}
} else { // We need 128 bits.
assert (64 < -exponent && -exponent <= 128);
final UInt128 fractionals128 = new UInt128(fractionals, 0);
fractionals128.shift(-exponent - 64);
int point = 128;
for (int i = 0; i < fractional_count; ++i) {
if (fractionals128.isZero()) break;
// As before: instead of multiplying by 10 we multiply by 5 and adjust the
// point location.
// This multiplication will not overflow for the same reasons as before.
fractionals128.multiply(5);
point--;
final int digit = fractionals128.divModPowerOf2(point);
assert (digit <= 9);
buffer.chars[buffer.length] = (char) ('0' + digit);
buffer.length++;
}
if (fractionals128.bitAt(point - 1) == 1) {
roundUp(buffer);
}
}
}
// Removes leading and trailing zeros.
// If leading zeros are removed then the decimal point position is adjusted.
static void trimZeros(final DtoaBuffer buffer) {
while (buffer.length > 0 && buffer.chars[buffer.length - 1] == '0') {
buffer.length--;
}
int first_non_zero = 0;
while (first_non_zero < buffer.length && buffer.chars[first_non_zero] == '0') {
first_non_zero++;
}
if (first_non_zero != 0) {
for (int i = first_non_zero; i < buffer.length; ++i) {
buffer.chars[i - first_non_zero] = buffer.chars[i];
}
buffer.length -= first_non_zero;
buffer.decimalPoint -= first_non_zero;
}
}
static boolean fastFixedDtoa(final double v,
final int fractional_count,
final DtoaBuffer buffer) {
final long kMaxUInt32 = 0xFFFFFFFFL;
final long l = IeeeDouble.doubleToLong(v);
long significand = IeeeDouble.significand(l);
final int exponent = IeeeDouble.exponent(l);
// v = significand * 2^exponent (with significand a 53bit integer).
// If the exponent is larger than 20 (i.e. we may have a 73bit number) then we
// don't know how to compute the representation. 2^73 ~= 9.5*10^21.
// If necessary this limit could probably be increased, but we don't need
// more.
if (exponent > 20) return false;
if (fractional_count > 20) return false;
// At most kDoubleSignificandSize bits of the significand are non-zero.
// Given a 64 bit integer we have 11 0s followed by 53 potentially non-zero
// bits: 0..11*..0xxx..53*..xx
if (exponent + kDoubleSignificandSize > 64) {
// The exponent must be > 11.
//
// We know that v = significand * 2^exponent.
// And the exponent > 11.
// We simplify the task by dividing v by 10^17.
// The quotient delivers the first digits, and the remainder fits into a 64
// bit number.
// Dividing by 10^17 is equivalent to dividing by 5^17*2^17.
final long kFive17 = 0xB1A2BC2EC5L; // 5^17
long divisor = kFive17;
final int divisor_power = 17;
long dividend = significand;
final int quotient;
final long remainder;
// Let v = f * 2^e with f == significand and e == exponent.
// Then need q (quotient) and r (remainder) as follows:
// v = q * 10^17 + r
// f * 2^e = q * 10^17 + r
// f * 2^e = q * 5^17 * 2^17 + r
// If e > 17 then
// f * 2^(e-17) = q * 5^17 + r/2^17
// else
// f = q * 5^17 * 2^(17-e) + r/2^e
if (exponent > divisor_power) {
// We only allow exponents of up to 20 and therefore (17 - e) <= 3
dividend <<= exponent - divisor_power;
quotient = (int) Long.divideUnsigned(dividend, divisor);
remainder = Long.remainderUnsigned(dividend, divisor) << divisor_power;
} else {
divisor <<= divisor_power - exponent;
quotient = (int) Long.divideUnsigned(dividend, divisor);
remainder = Long.remainderUnsigned(dividend, divisor) << exponent;
}
fillDigits32(quotient, buffer);
fillDigits64FixedLength(remainder, buffer);
buffer.decimalPoint = buffer.length;
} else if (exponent >= 0) {
// 0 <= exponent <= 11
significand <<= exponent;
FillDigits64(significand, buffer);
buffer.decimalPoint = buffer.length;
} else if (exponent > -kDoubleSignificandSize) {
// We have to cut the number.
final long integrals = significand >>> -exponent;
final long fractionals = significand - (integrals << -exponent);
if (Long.compareUnsigned(integrals, kMaxUInt32) > 0) {
FillDigits64(integrals, buffer);
} else {
fillDigits32((int) (integrals), buffer);
}
buffer.decimalPoint = buffer.length;
fillFractionals(fractionals, exponent, fractional_count, buffer);
} else if (exponent < -128) {
// This configuration (with at most 20 digits) means that all digits must be
// 0.
assert (fractional_count <= 20);
buffer.reset();
buffer.decimalPoint = -fractional_count;
} else {
buffer.decimalPoint = 0;
fillFractionals(significand, exponent, fractional_count, buffer);
}
trimZeros(buffer);
if (buffer.length == 0) {
// The string is empty and the decimal_point thus has no importance. Mimick
// Gay's dtoa and and set it to -fractional_count.
buffer.decimalPoint = -fractional_count;
}
return true;
}
}

@ -0,0 +1,233 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jdk.nashorn.internal.runtime.doubleconv;
// Helper functions for doubles.
class IeeeDouble {
// We assume that doubles and long have the same endianness.
static long doubleToLong(final double d) { return Double.doubleToRawLongBits(d); }
static double longToDouble(final long d64) { return Double.longBitsToDouble(d64); }
static final long kSignMask = 0x8000000000000000L;
static final long kExponentMask = 0x7FF0000000000000L;
static final long kSignificandMask = 0x000FFFFFFFFFFFFFL;
static final long kHiddenBit = 0x0010000000000000L;
static final int kPhysicalSignificandSize = 52; // Excludes the hidden bit.
static final int kSignificandSize = 53;
static private final int kExponentBias = 0x3FF + kPhysicalSignificandSize;
static private final int kDenormalExponent = -kExponentBias + 1;
static private final int kMaxExponent = 0x7FF - kExponentBias;
static private final long kInfinity = 0x7FF0000000000000L;
static private final long kNaN = 0x7FF8000000000000L;
static DiyFp asDiyFp(final long d64) {
assert (!isSpecial(d64));
return new DiyFp(significand(d64), exponent(d64));
}
// The value encoded by this Double must be strictly greater than 0.
static DiyFp asNormalizedDiyFp(final long d64) {
assert (value(d64) > 0.0);
long f = significand(d64);
int e = exponent(d64);
// The current double could be a denormal.
while ((f & kHiddenBit) == 0) {
f <<= 1;
e--;
}
// Do the final shifts in one go.
f <<= DiyFp.kSignificandSize - kSignificandSize;
e -= DiyFp.kSignificandSize - kSignificandSize;
return new DiyFp(f, e);
}
// Returns the next greater double. Returns +infinity on input +infinity.
static double nextDouble(final long d64) {
if (d64 == kInfinity) return longToDouble(kInfinity);
if (sign(d64) < 0 && significand(d64) == 0) {
// -0.0
return 0.0;
}
if (sign(d64) < 0) {
return longToDouble(d64 - 1);
} else {
return longToDouble(d64 + 1);
}
}
static double previousDouble(final long d64) {
if (d64 == (kInfinity | kSignMask)) return -longToDouble(kInfinity);
if (sign(d64) < 0) {
return longToDouble(d64 + 1);
} else {
if (significand(d64) == 0) return -0.0;
return longToDouble(d64 - 1);
}
}
static int exponent(final long d64) {
if (isDenormal(d64)) return kDenormalExponent;
final int biased_e = (int) ((d64 & kExponentMask) >>> kPhysicalSignificandSize);
return biased_e - kExponentBias;
}
static long significand(final long d64) {
final long significand = d64 & kSignificandMask;
if (!isDenormal(d64)) {
return significand + kHiddenBit;
} else {
return significand;
}
}
// Returns true if the double is a denormal.
static boolean isDenormal(final long d64) {
return (d64 & kExponentMask) == 0L;
}
// We consider denormals not to be special.
// Hence only Infinity and NaN are special.
static boolean isSpecial(final long d64) {
return (d64 & kExponentMask) == kExponentMask;
}
static boolean isNaN(final long d64) {
return ((d64 & kExponentMask) == kExponentMask) &&
((d64 & kSignificandMask) != 0L);
}
static boolean isInfinite(final long d64) {
return ((d64 & kExponentMask) == kExponentMask) &&
((d64 & kSignificandMask) == 0L);
}
static int sign(final long d64) {
return (d64 & kSignMask) == 0L ? 1 : -1;
}
// Computes the two boundaries of this.
// The bigger boundary (m_plus) is normalized. The lower boundary has the same
// exponent as m_plus.
// Precondition: the value encoded by this Double must be greater than 0.
static void normalizedBoundaries(final long d64, final DiyFp m_minus, final DiyFp m_plus) {
assert (value(d64) > 0.0);
final DiyFp v = asDiyFp(d64);
m_plus.setF((v.f() << 1) + 1);
m_plus.setE(v.e() - 1);
m_plus.normalize();
if (lowerBoundaryIsCloser(d64)) {
m_minus.setF((v.f() << 2) - 1);
m_minus.setE(v.e() - 2);
} else {
m_minus.setF((v.f() << 1) - 1);
m_minus.setE(v.e() - 1);
}
m_minus.setF(m_minus.f() << (m_minus.e() - m_plus.e()));
m_minus.setE(m_plus.e());
}
static boolean lowerBoundaryIsCloser(final long d64) {
// The boundary is closer if the significand is of the form f == 2^p-1 then
// the lower boundary is closer.
// Think of v = 1000e10 and v- = 9999e9.
// Then the boundary (== (v - v-)/2) is not just at a distance of 1e9 but
// at a distance of 1e8.
// The only exception is for the smallest normal: the largest denormal is
// at the same distance as its successor.
// Note: denormals have the same exponent as the smallest normals.
final boolean physical_significand_is_zero = ((d64 & kSignificandMask) == 0);
return physical_significand_is_zero && (exponent(d64) != kDenormalExponent);
}
static double value(final long d64) {
return longToDouble(d64);
}
// Returns the significand size for a given order of magnitude.
// If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude.
// This function returns the number of significant binary digits v will have
// once it's encoded into a double. In almost all cases this is equal to
// kSignificandSize. The only exceptions are denormals. They start with
// leading zeroes and their effective significand-size is hence smaller.
static int significandSizeForOrderOfMagnitude(final int order) {
if (order >= (kDenormalExponent + kSignificandSize)) {
return kSignificandSize;
}
if (order <= kDenormalExponent) return 0;
return order - kDenormalExponent;
}
static double Infinity() {
return longToDouble(kInfinity);
}
static double NaN() {
return longToDouble(kNaN);
}
}

@ -114,7 +114,7 @@ SyntaxError: Invalid JSON: <json>:1:9 Expected , or } but found r
{"a":truer}
^
[1,2,3]
[9223372036854773800,9223372036854774800,9223372036854776000]
[9223372036854774000,9223372036854775000,9223372036854776000]
[1.1,1.2,1.3]
[1,1.2,9223372036854776000,null,true]
{"a":"string","b":1,"c":1.2,"d":9223372036854776000,"e":null,"f":true}

@ -35,6 +35,6 @@ var x = -1.23456789e+21;
var y = -1.23456789e+20;
print(x.toFixed(9));
print(y.toFixed(9).indexOf(",") === -1); // no grouping
//print(y.toFixed(9)); // FIXME expected: -123456788999999995904.000000000
//print(1000000000000000128); // FIXME expected: 1000000000000000100
//print((1000000000000000128).toFixed(0)); // FIXME expected: 1000000000000000128
print(y.toFixed(9));
print(1000000000000000128);
print((1000000000000000128).toFixed(0));

@ -3,3 +3,6 @@
0.00001
-1.23456789e+21
true
-123456788999999995904.000000000
1000000000000000100
1000000000000000128

@ -0,0 +1,355 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jdk.nashorn.internal.runtime.doubleconv.test;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import jdk.nashorn.internal.runtime.doubleconv.DoubleConversion;
import jdk.nashorn.internal.runtime.doubleconv.DtoaBuffer;
import jdk.nashorn.internal.runtime.doubleconv.DtoaMode;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
/**
* FastDtoa tests
*
* @test
* @run testng jdk.nashorn.internal.runtime.doubleconv.test.FastDtoaTest
*/
@SuppressWarnings("javadoc")
public class BignumDtoaTest {
final static private int BUFFER_SIZE = 100;
// Removes trailing '0' digits.
// Can return the empty string if all digits are 0.
private static String trimRepresentation(final String representation) {
final int len = representation.length();
int i;
for (i = len - 1; i >= 0; --i) {
if (representation.charAt(i) != '0') break;
}
return representation.substring(0, i + 1);
}
@Test
public void testBignumVarious() {
final DtoaBuffer buffer = new DtoaBuffer(BUFFER_SIZE);
DoubleConversion.bignumDtoa(1, DtoaMode.SHORTEST, 0, buffer);
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(1.0, DtoaMode.FIXED, 3, buffer);
assertTrue(3 >= buffer.getLength() - buffer.getDecimalPoint());
assertEquals("1", trimRepresentation(buffer.getRawDigits()));
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(1.0, DtoaMode.PRECISION, 3, buffer);
assertTrue(3 >= buffer.getLength());
assertEquals("1", trimRepresentation(buffer.getRawDigits()));
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(1.5, DtoaMode.SHORTEST, 0, buffer);
assertEquals("15", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(1.5, DtoaMode.FIXED, 10, buffer);
assertTrue(10 >= buffer.getLength() - buffer.getDecimalPoint());
assertEquals("15", trimRepresentation(buffer.getRawDigits()));
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(1.5, DtoaMode.PRECISION, 10, buffer);
assertTrue(10 >= buffer.getLength());
assertEquals("15", trimRepresentation(buffer.getRawDigits()));
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
final double min_double = 5e-324;
DoubleConversion.bignumDtoa(min_double, DtoaMode.SHORTEST, 0, buffer);
assertEquals("5", buffer.getRawDigits());
assertEquals(-323, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(min_double, DtoaMode.FIXED, 5, buffer);
assertTrue(5 >= buffer.getLength() - buffer.getDecimalPoint());
assertEquals("", trimRepresentation(buffer.getRawDigits()));
buffer.reset();
DoubleConversion.bignumDtoa(min_double, DtoaMode.PRECISION, 5, buffer);
assertTrue(5 >= buffer.getLength());
assertEquals("49407", trimRepresentation(buffer.getRawDigits()));
assertEquals(-323, buffer.getDecimalPoint());
buffer.reset();
final double max_double = 1.7976931348623157e308;
DoubleConversion.bignumDtoa(max_double, DtoaMode.SHORTEST, 0, buffer);
assertEquals("17976931348623157", buffer.getRawDigits());
assertEquals(309, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(max_double, DtoaMode.PRECISION, 7, buffer);
assertTrue(7 >= buffer.getLength());
assertEquals("1797693", trimRepresentation(buffer.getRawDigits()));
assertEquals(309, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(4294967272.0, DtoaMode.SHORTEST, 0, buffer);
assertEquals("4294967272", buffer.getRawDigits());
assertEquals(10, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(4294967272.0, DtoaMode.FIXED, 5, buffer);
assertEquals("429496727200000", buffer.getRawDigits());
assertEquals(10, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(4294967272.0, DtoaMode.PRECISION, 14, buffer);
assertTrue(14 >= buffer.getLength());
assertEquals("4294967272", trimRepresentation(buffer.getRawDigits()));
assertEquals(10, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(4.1855804968213567e298, DtoaMode.SHORTEST, 0, buffer);
assertEquals("4185580496821357", buffer.getRawDigits());
assertEquals(299, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(4.1855804968213567e298, DtoaMode.PRECISION, 20, buffer);
assertTrue(20 >= buffer.getLength());
assertEquals("41855804968213567225", trimRepresentation(buffer.getRawDigits()));
assertEquals(299, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(5.5626846462680035e-309, DtoaMode.SHORTEST, 0, buffer);
assertEquals("5562684646268003", buffer.getRawDigits());
assertEquals(-308, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(5.5626846462680035e-309, DtoaMode.PRECISION, 1, buffer);
assertTrue(1 >= buffer.getLength());
assertEquals("6", trimRepresentation(buffer.getRawDigits()));
assertEquals(-308, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(2147483648.0, DtoaMode.SHORTEST, 0, buffer);
assertEquals("2147483648", buffer.getRawDigits());
assertEquals(10, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(2147483648.0, DtoaMode.FIXED, 2, buffer);
assertTrue(2 >= buffer.getLength() - buffer.getDecimalPoint());
assertEquals("2147483648", trimRepresentation(buffer.getRawDigits()));
assertEquals(10, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(2147483648.0, DtoaMode.PRECISION, 5, buffer);
assertTrue(5 >= buffer.getLength());
assertEquals("21475", trimRepresentation(buffer.getRawDigits()));
assertEquals(10, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(3.5844466002796428e+298, DtoaMode.SHORTEST, 0, buffer);
assertEquals("35844466002796428", buffer.getRawDigits());
assertEquals(299, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(3.5844466002796428e+298, DtoaMode.PRECISION, 10, buffer);
assertTrue(10 >= buffer.getLength());
assertEquals("35844466", trimRepresentation(buffer.getRawDigits()));
assertEquals(299, buffer.getDecimalPoint());
buffer.reset();
final long smallest_normal64 = 0x0010000000000000L;
double v = Double.longBitsToDouble(smallest_normal64);
DoubleConversion.bignumDtoa(v, DtoaMode.SHORTEST, 0, buffer);
assertEquals("22250738585072014", buffer.getRawDigits());
assertEquals(-307, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(v, DtoaMode.PRECISION, 20, buffer);
assertTrue(20 >= buffer.getLength());
assertEquals("22250738585072013831", trimRepresentation(buffer.getRawDigits()));
assertEquals(-307, buffer.getDecimalPoint());
buffer.reset();
final long largest_denormal64 = 0x000FFFFFFFFFFFFFL;
v = Double.longBitsToDouble(largest_denormal64);
DoubleConversion.bignumDtoa(v, DtoaMode.SHORTEST, 0, buffer);
assertEquals("2225073858507201", buffer.getRawDigits());
assertEquals(-307, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(v, DtoaMode.PRECISION, 20, buffer);
assertTrue(20 >= buffer.getLength());
assertEquals("2225073858507200889", trimRepresentation(buffer.getRawDigits()));
assertEquals(-307, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(4128420500802942e-24, DtoaMode.SHORTEST, 0, buffer);
assertEquals("4128420500802942", buffer.getRawDigits());
assertEquals(-8, buffer.getDecimalPoint());
buffer.reset();
DoubleConversion.bignumDtoa(3.9292015898194142585311918e-10, DtoaMode.SHORTEST, 0, buffer);
assertEquals("39292015898194143", buffer.getRawDigits());
buffer.reset();
v = 4194304.0;
DoubleConversion.bignumDtoa(v, DtoaMode.FIXED, 5, buffer);
assertTrue(5 >= buffer.getLength() - buffer.getDecimalPoint());
assertEquals("4194304", trimRepresentation(buffer.getRawDigits()));
buffer.reset();
v = 3.3161339052167390562200598e-237;
DoubleConversion.bignumDtoa(v, DtoaMode.PRECISION, 19, buffer);
assertTrue(19 >= buffer.getLength());
assertEquals("3316133905216739056", trimRepresentation(buffer.getRawDigits()));
assertEquals(-236, buffer.getDecimalPoint());
buffer.reset();
v = 7.9885183916008099497815232e+191;
DoubleConversion.bignumDtoa(v, DtoaMode.PRECISION, 4, buffer);
assertTrue(4 >= buffer.getLength());
assertEquals("7989", trimRepresentation(buffer.getRawDigits()));
assertEquals(192, buffer.getDecimalPoint());
buffer.reset();
v = 1.0000000000000012800000000e+17;
DoubleConversion.bignumDtoa(v, DtoaMode.FIXED, 1, buffer);
assertTrue(1 >= buffer.getLength() - buffer.getDecimalPoint());
assertEquals("100000000000000128", trimRepresentation(buffer.getRawDigits()));
assertEquals(18, buffer.getDecimalPoint());
buffer.reset();
}
@Test
public void testBignumShortest() {
new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("resources/gay-shortest.txt")))
.lines()
.forEach(line -> {
if (line.isEmpty() || line.startsWith("//")) {
return; // comment or empty line
}
final String[] tokens = line.split(",\\s+");
assertEquals(tokens.length, 3, "*" + line + "*");
final double v = Double.parseDouble(tokens[0]);
final String str = tokens[1].replace('"', ' ').trim();;
final int point = Integer.parseInt(tokens[2]);
final DtoaBuffer buffer = new DtoaBuffer(BUFFER_SIZE);
DoubleConversion.bignumDtoa(v, DtoaMode.SHORTEST, 0, buffer);
assertEquals(str, buffer.getRawDigits());
assertEquals(point, buffer.getDecimalPoint());
});
}
@Test
public void testBignumFixed() {
new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("resources/gay-fixed.txt")))
.lines()
.forEach(line -> {
if (line.isEmpty() || line.startsWith("//")) {
return; // comment or empty line
}
final String[] tokens = line.split(",\\s+");
assertEquals(tokens.length, 4);
final double v = Double.parseDouble(tokens[0]);
final int digits = Integer.parseInt(tokens[1]);
final String str = tokens[2].replace('"', ' ').trim();
final int point = Integer.parseInt(tokens[3]);
final DtoaBuffer buffer = new DtoaBuffer(BUFFER_SIZE);
DoubleConversion.bignumDtoa(v, DtoaMode.FIXED, digits, buffer);
assertEquals(str, trimRepresentation(buffer.getRawDigits()));
assertEquals(point, buffer.getDecimalPoint());
});
}
@Test
public void testBignumPrecision() {
new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("resources/gay-precision.txt")))
.lines()
.forEach(line -> {
if (line.isEmpty() || line.startsWith("//")) {
return; // comment or empty line
}
final String[] tokens = line.split(",\\s+");
assertEquals(tokens.length, 4);
final double v = Double.parseDouble(tokens[0]);
final int digits = Integer.parseInt(tokens[1]);
final String str = tokens[2].replace('"', ' ').trim();
final int point = Integer.parseInt(tokens[3]);
final DtoaBuffer buffer = new DtoaBuffer(BUFFER_SIZE);
DoubleConversion.bignumDtoa(v, DtoaMode.PRECISION, digits, buffer);
assertEquals(str, trimRepresentation(buffer.getRawDigits()));
assertEquals(point, buffer.getDecimalPoint());
});
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,123 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2006-2008 the V8 project authors. All rights reserved.
package jdk.nashorn.internal.runtime.doubleconv.test;
import org.testng.annotations.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
/**
* DiyFp class tests
*
* @test
* @run testng jdk.nashorn.internal.runtime.doubleconv.test.DiyFpTest
*/
@SuppressWarnings("javadoc")
public class DiyFpTest {
static final Class<?> DiyFp;
static final Constructor<?> ctor;
static {
try {
DiyFp = Class.forName("jdk.nashorn.internal.runtime.doubleconv.DiyFp");
ctor = DiyFp.getDeclaredConstructor(long.class, int.class);
ctor.setAccessible(true);
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
private static Method method(final String name, final Class<?>... params) throws NoSuchMethodException {
final Method m = DiyFp.getDeclaredMethod(name, params);
m.setAccessible(true);
return m;
}
@Test
public void testSubtract() throws Exception {
final Object diyFp1 = ctor.newInstance(3, 0);
final Object diyFp2 = ctor.newInstance(1, 0);
final Object diff = method("minus", DiyFp, DiyFp).invoke(null, diyFp1, diyFp2);;
assertTrue(2l == (long) method("f").invoke(diff));
assertTrue(0 == (int) method("e").invoke(diff));
method("subtract", DiyFp).invoke(diyFp1, diyFp2);
assertTrue(2l == (long) method("f").invoke(diyFp1));
assertTrue(0 == (int) method("e").invoke(diyFp2));
}
@Test
public void testMultiply() throws Exception {
Object diyFp1, diyFp2, product;
diyFp1 = ctor.newInstance(3, 0);
diyFp2 = ctor.newInstance(2, 0);
product = method("times", DiyFp, DiyFp).invoke(null, diyFp1, diyFp2);
assertEquals(0l, (long) method("f").invoke(product));
assertEquals(64, (int) method("e").invoke(product));
method("multiply", DiyFp).invoke(diyFp1, diyFp2);
assertEquals(0l, (long) method("f").invoke(diyFp1));
assertEquals(64, (int) method("e").invoke(diyFp1));
diyFp1 = ctor.newInstance(0x8000000000000000L, 11);
diyFp2 = ctor.newInstance(2, 13);
product = method("times", DiyFp, DiyFp).invoke(null, diyFp1, diyFp2);
assertEquals(1l, (long) method("f").invoke(product));
assertEquals(11 + 13 + 64, (int) method("e").invoke(product));
// Test rounding.
diyFp1 = ctor.newInstance(0x8000000000000001L, 11);
diyFp2 = ctor.newInstance(1, 13);
product = method("times", DiyFp, DiyFp).invoke(null, diyFp1, diyFp2);
assertEquals(1l, (long) method("f").invoke(product));
assertEquals(11 + 13 + 64, (int) method("e").invoke(product));
diyFp1 = ctor.newInstance(0x7fffffffffffffffL, 11);
diyFp2 = ctor.newInstance(1, 13);
product = method("times", DiyFp, DiyFp).invoke(null, diyFp1, diyFp2);
assertEquals(0l, (long) method("f").invoke(product));
assertEquals(11 + 13 + 64, (int) method("e").invoke(product));
// Big numbers.
diyFp1 = ctor.newInstance(0xFFFFFFFFFFFFFFFFL, 11);
diyFp2 = ctor.newInstance(0xFFFFFFFFFFFFFFFFL, 13);
// 128bit result: 0xfffffffffffffffe0000000000000001
product = method("times", DiyFp, DiyFp).invoke(null, diyFp1, diyFp2);
assertEquals(0xFFFFFFFFFFFFFFFel, (long) method("f").invoke(product));
assertEquals(11 + 13 + 64, (int) method("e").invoke(product));
}
}

@ -0,0 +1,327 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2006-2008 the V8 project authors. All rights reserved.
package jdk.nashorn.internal.runtime.doubleconv.test;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import jdk.nashorn.internal.runtime.doubleconv.DoubleConversion;
import jdk.nashorn.internal.runtime.doubleconv.DtoaBuffer;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
/**
* FastDtoa tests
*
* @test
* @run testng jdk.nashorn.internal.runtime.doubleconv.test.FastDtoaTest
*/
@SuppressWarnings("javadoc")
public class FastDtoaTest {
final static private int kBufferSize = 100;
// Removes trailing '0' digits.
// Can return the empty string if all digits are 0.
private static String trimRepresentation(final String representation) {
final int len = representation.length();
int i;
for (i = len - 1; i >= 0; --i) {
if (representation.charAt(i) != '0') break;
}
return representation.substring(0, i + 1);
}
@Test
public void testFastShortestVarious() {
final DtoaBuffer buffer = new DtoaBuffer(kBufferSize);
boolean status;
final double min_double = 5e-324;
status = DoubleConversion.fastDtoaShortest(min_double, buffer);
assertTrue(status);
assertEquals("5", buffer.getRawDigits());
assertEquals(-323, buffer.getDecimalPoint());
buffer.reset();
final double max_double = 1.7976931348623157e308;
status = DoubleConversion.fastDtoaShortest(max_double, buffer);
assertTrue(status);
assertEquals("17976931348623157", buffer.getRawDigits());
assertEquals(309, buffer.getDecimalPoint());
buffer.reset();
status = DoubleConversion.fastDtoaShortest(4294967272.0, buffer);
assertTrue(status);
assertEquals("4294967272", buffer.getRawDigits());
assertEquals(10, buffer.getDecimalPoint());
buffer.reset();
status = DoubleConversion.fastDtoaShortest(4.1855804968213567e298, buffer);
assertTrue(status);
assertEquals("4185580496821357", buffer.getRawDigits());
assertEquals(299, buffer.getDecimalPoint());
buffer.reset();
status = DoubleConversion.fastDtoaShortest(5.5626846462680035e-309, buffer);
assertTrue(status);
assertEquals("5562684646268003", buffer.getRawDigits());
assertEquals(-308, buffer.getDecimalPoint());
buffer.reset();
status = DoubleConversion.fastDtoaShortest(2147483648.0, buffer);
assertTrue(status);
assertEquals("2147483648", buffer.getRawDigits());
assertEquals(10, buffer.getDecimalPoint());
buffer.reset();
status = DoubleConversion.fastDtoaShortest(3.5844466002796428e+298, buffer);
if (status) { // Not all FastDtoa variants manage to compute this number.
assertEquals("35844466002796428", buffer.getRawDigits());
assertEquals(299, buffer.getDecimalPoint());
}
buffer.reset();
final long smallest_normal64 = 0x0010000000000000L;
double v = Double.longBitsToDouble(smallest_normal64);
status = DoubleConversion.fastDtoaShortest(v, buffer);
if (status) {
assertEquals("22250738585072014", buffer.getRawDigits());
assertEquals(-307, buffer.getDecimalPoint());
}
buffer.reset();
final long largest_denormal64 = 0x000FFFFFFFFFFFFFL;
v = Double.longBitsToDouble(largest_denormal64);
status = DoubleConversion.fastDtoaShortest(v, buffer);
if (status) {
assertEquals("2225073858507201", buffer.getRawDigits());
assertEquals(-307, buffer.getDecimalPoint());
}
buffer.reset();
}
@Test
public void testFastPrecisionVarious() {
final DtoaBuffer buffer = new DtoaBuffer(kBufferSize);
boolean status;
status = DoubleConversion.fastDtoaCounted(1.0, 3, buffer);
assertTrue(status);
assertTrue(3 >= buffer.getLength());
assertEquals("1", trimRepresentation(buffer.getRawDigits()));
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
status = DoubleConversion.fastDtoaCounted(1.5, 10, buffer);
if (status) {
assertTrue(10 >= buffer.getLength());
assertEquals("15", trimRepresentation(buffer.getRawDigits()));
assertEquals(1, buffer.getDecimalPoint());
}
buffer.reset();
final double min_double = 5e-324;
status = DoubleConversion.fastDtoaCounted(min_double, 5, buffer);
assertTrue(status);
assertEquals("49407", buffer.getRawDigits());
assertEquals(-323, buffer.getDecimalPoint());
buffer.reset();
final double max_double = 1.7976931348623157e308;
status = DoubleConversion.fastDtoaCounted(max_double, 7, buffer);
assertTrue(status);
assertEquals("1797693", buffer.getRawDigits());
assertEquals(309, buffer.getDecimalPoint());
buffer.reset();
status = DoubleConversion.fastDtoaCounted(4294967272.0, 14, buffer);
if (status) {
assertTrue(14 >= buffer.getLength());
assertEquals("4294967272", trimRepresentation(buffer.getRawDigits()));
assertEquals(10, buffer.getDecimalPoint());
}
buffer.reset();
status = DoubleConversion.fastDtoaCounted(4.1855804968213567e298, 17, buffer);
assertTrue(status);
assertEquals("41855804968213567", buffer.getRawDigits());
assertEquals(299, buffer.getDecimalPoint());
buffer.reset();
status = DoubleConversion.fastDtoaCounted(5.5626846462680035e-309, 1, buffer);
assertTrue(status);
assertEquals("6", buffer.getRawDigits());
assertEquals(-308, buffer.getDecimalPoint());
buffer.reset();
status = DoubleConversion.fastDtoaCounted(2147483648.0, 5, buffer);
assertTrue(status);
assertEquals("21475", buffer.getRawDigits());
assertEquals(10, buffer.getDecimalPoint());
buffer.reset();
status = DoubleConversion.fastDtoaCounted(3.5844466002796428e+298, 10, buffer);
assertTrue(status);
assertTrue(10 >= buffer.getLength());
assertEquals("35844466", trimRepresentation(buffer.getRawDigits()));
assertEquals(299, buffer.getDecimalPoint());
buffer.reset();
final long smallest_normal64 = 0x0010000000000000L;
double v = Double.longBitsToDouble(smallest_normal64);
status = DoubleConversion.fastDtoaCounted(v, 17, buffer);
assertTrue(status);
assertEquals("22250738585072014", buffer.getRawDigits());
assertEquals(-307, buffer.getDecimalPoint());
buffer.reset();
final long largest_denormal64 = 0x000FFFFFFFFFFFFFL;
v = Double.longBitsToDouble(largest_denormal64);
status = DoubleConversion.fastDtoaCounted(v, 17, buffer);
assertTrue(status);
assertTrue(20 >= buffer.getLength());
assertEquals("22250738585072009", trimRepresentation(buffer.getRawDigits()));
assertEquals(-307, buffer.getDecimalPoint());
buffer.reset();
v = 3.3161339052167390562200598e-237;
status = DoubleConversion.fastDtoaCounted(v, 18, buffer);
assertTrue(status);
assertEquals("331613390521673906", buffer.getRawDigits());
assertEquals(-236, buffer.getDecimalPoint());
buffer.reset();
v = 7.9885183916008099497815232e+191;
status = DoubleConversion.fastDtoaCounted(v, 4, buffer);
assertTrue(status);
assertEquals("7989", buffer.getRawDigits());
assertEquals(192, buffer.getDecimalPoint());
buffer.reset();
}
@Test
public void testFastShortest() {
final AtomicInteger total = new AtomicInteger();
final AtomicInteger succeeded = new AtomicInteger();
final AtomicBoolean neededMaxLength = new AtomicBoolean();
new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("resources/gay-shortest.txt")))
.lines()
.forEach(line -> {
if (line.isEmpty() || line.startsWith("//")) {
return; // comment or empty line
}
final String[] tokens = line.split(",\\s+");
assertEquals(tokens.length, 3, "*" + line + "*");
final double v = Double.parseDouble(tokens[0]);
final String str = tokens[1].replace('"', ' ').trim();;
final int point = Integer.parseInt(tokens[2]);
final DtoaBuffer buffer = new DtoaBuffer(kBufferSize);
total.getAndIncrement();
if (DoubleConversion.fastDtoaShortest(v, buffer)) {
assertEquals(str, buffer.getRawDigits());
assertEquals(point, buffer.getDecimalPoint());
succeeded.getAndIncrement();
if (buffer.getLength() == DtoaBuffer.kFastDtoaMaximalLength) {
neededMaxLength.set(true);
}
}
});
assertTrue(succeeded.get() * 1.0 / total.get() > 0.99);
assertTrue(neededMaxLength.get());
// Additional constraints: Make sure these numbers are exactly the same as in C++ version
assertEquals(succeeded.get(), 99440);
assertEquals(total.get(), 100000);
}
@Test
public void testFastPrecision() {
final AtomicInteger total = new AtomicInteger();
final AtomicInteger succeeded = new AtomicInteger();
// Count separately for entries with less than 15 requested digits.
final AtomicInteger succeeded_15 = new AtomicInteger();
final AtomicInteger total_15 = new AtomicInteger();
new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("resources/gay-precision.txt")))
.lines()
.forEach(line -> {
if (line.isEmpty() || line.startsWith("//")) {
return; // comment or empty line
}
final String[] tokens = line.split(",\\s+");
assertEquals(tokens.length, 4);
final double v = Double.parseDouble(tokens[0]);
final int digits = Integer.parseInt(tokens[1]);
final String str = tokens[2].replace('"', ' ').trim();
final int point = Integer.parseInt(tokens[3]);
final DtoaBuffer buffer = new DtoaBuffer(kBufferSize);
total.getAndIncrement();
if (digits <= 15) {
total_15.getAndIncrement();
}
if (DoubleConversion.fastDtoaCounted(v, digits, buffer)) {
assertEquals(str, trimRepresentation(buffer.getRawDigits()));
assertEquals(point, buffer.getDecimalPoint());
succeeded.getAndIncrement();
if (digits <= 15) {
succeeded_15.getAndIncrement();
}
}
});
// The precomputed numbers contain many entries with many requested
// digits. These have a high failure rate and we therefore expect a lower
// success rate than for the shortest representation.
assertTrue(succeeded.get() * 1.0 / total.get() > 0.85);
// However with less than 15 digits almost the algorithm should almost always
// succeed.
assertTrue(succeeded_15.get() * 1.0 / total_15.get() > 0.9999);
// Additional constraints: Make sure these numbers are exactly the same as in C++ version
assertEquals(succeeded.get(), 86866);
assertEquals(total.get(), 100000);
assertEquals(succeeded_15.get(), 71328);
assertEquals(total_15.get(), 71330);
}
}

@ -0,0 +1,665 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jdk.nashorn.internal.runtime.doubleconv.test;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.atomic.AtomicInteger;
import jdk.nashorn.internal.runtime.doubleconv.DoubleConversion;
import jdk.nashorn.internal.runtime.doubleconv.DtoaBuffer;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
/**
* FixedDtoa tests
*
* @test
* @run testng jdk.nashorn.internal.runtime.doubleconv.test.FixedDtoaTest
*/
@SuppressWarnings("javadoc")
public class FixedDtoaTest {
static final int kBufferSize = 500;
@Test
public void testFastShortestVarious() {
final DtoaBuffer buffer = new DtoaBuffer(kBufferSize);
assertTrue(DoubleConversion.fixedDtoa(1.0, 1, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1.0, 15, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1.0, 0, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0xFFFFFFFFL, 5, buffer));
assertEquals("4294967295", buffer.getRawDigits());
assertEquals(10, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(4294967296.0, 5, buffer));
assertEquals("4294967296", buffer.getRawDigits());
assertEquals(10, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1e21, 5, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(22, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(999999999999999868928.00, 2, buffer));
assertEquals("999999999999999868928", buffer.getRawDigits());
assertEquals(21, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(6.9999999999999989514240000e+21, 5, buffer));
assertEquals("6999999999999998951424", buffer.getRawDigits());
assertEquals(22, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1.5, 5, buffer));
assertEquals("15", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1.55, 5, buffer));
assertEquals("155", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1.55, 1, buffer));
assertEquals("16", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1.00000001, 15, buffer));
assertEquals("100000001", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.1, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(0, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.01, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.001, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-2, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0001, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-3, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00001, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-4, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000001, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-5, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000001, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-6, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00000001, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-7, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000001, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-8, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000000001, 15, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-9, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00000000001, 15, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-10, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000001, 15, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-11, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000000000001, 15, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-12, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00000000000001, 15, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-13, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000000001, 20, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-14, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000000000000001, 20, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-15, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00000000000000001, 20, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-16, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000000000001, 20, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-17, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000000000000000001, 20, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-18, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00000000000000000001, 20, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-19, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.10000000004, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(0, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.01000000004, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00100000004, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-2, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00010000004, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-3, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00001000004, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-4, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00000100004, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-5, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00000010004, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-6, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00000001004, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-7, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00000000104, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-8, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000000001000004, 15, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-9, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000000000100004, 15, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-10, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000000000010004, 15, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-11, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000000000001004, 15, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-12, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000000000000104, 15, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-13, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000000001000004, 20, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-14, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000000000100004, 20, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-15, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000000000010004, 20, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-16, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000000000001004, 20, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-17, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000000000000104, 20, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-18, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000000000000014, 20, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-19, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.10000000006, 10, buffer));
assertEquals("1000000001", buffer.getRawDigits());
assertEquals(0, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.01000000006, 10, buffer));
assertEquals("100000001", buffer.getRawDigits());
assertEquals(-1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00100000006, 10, buffer));
assertEquals("10000001", buffer.getRawDigits());
assertEquals(-2, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00010000006, 10, buffer));
assertEquals("1000001", buffer.getRawDigits());
assertEquals(-3, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00001000006, 10, buffer));
assertEquals("100001", buffer.getRawDigits());
assertEquals(-4, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00000100006, 10, buffer));
assertEquals("10001", buffer.getRawDigits());
assertEquals(-5, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00000010006, 10, buffer));
assertEquals("1001", buffer.getRawDigits());
assertEquals(-6, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00000001006, 10, buffer));
assertEquals("101", buffer.getRawDigits());
assertEquals(-7, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00000000106, 10, buffer));
assertEquals("11", buffer.getRawDigits());
assertEquals(-8, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000000001000006, 15, buffer));
assertEquals("100001", buffer.getRawDigits());
assertEquals(-9, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000000000100006, 15, buffer));
assertEquals("10001", buffer.getRawDigits());
assertEquals(-10, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000000000010006, 15, buffer));
assertEquals("1001", buffer.getRawDigits());
assertEquals(-11, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000000000001006, 15, buffer));
assertEquals("101", buffer.getRawDigits());
assertEquals(-12, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000000000000106, 15, buffer));
assertEquals("11", buffer.getRawDigits());
assertEquals(-13, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000000001000006, 20, buffer));
assertEquals("100001", buffer.getRawDigits());
assertEquals(-14, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000000000100006, 20, buffer));
assertEquals("10001", buffer.getRawDigits());
assertEquals(-15, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000000000010006, 20, buffer));
assertEquals("1001", buffer.getRawDigits());
assertEquals(-16, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000000000001006, 20, buffer));
assertEquals("101", buffer.getRawDigits());
assertEquals(-17, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000000000000106, 20, buffer));
assertEquals("11", buffer.getRawDigits());
assertEquals(-18, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000000000000000016, 20, buffer));
assertEquals("2", buffer.getRawDigits());
assertEquals(-19, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.6, 0, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.96, 1, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.996, 2, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.9996, 3, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.99996, 4, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.999996, 5, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.9999996, 6, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.99999996, 7, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.999999996, 8, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.9999999996, 9, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.99999999996, 10, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.999999999996, 11, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.9999999999996, 12, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.99999999999996, 13, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.999999999999996, 14, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.9999999999999996, 15, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00999999999999996, 16, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000999999999999996, 17, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-2, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.0000999999999999996, 18, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-3, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.00000999999999999996, 19, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-4, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.000000999999999999996, 20, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-5, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(323423.234234, 10, buffer));
assertEquals("323423234234", buffer.getRawDigits());
assertEquals(6, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(12345678.901234, 4, buffer));
assertEquals("123456789012", buffer.getRawDigits());
assertEquals(8, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(98765.432109, 5, buffer));
assertEquals("9876543211", buffer.getRawDigits());
assertEquals(5, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(42, 20, buffer));
assertEquals("42", buffer.getRawDigits());
assertEquals(2, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(0.5, 0, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(1, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1e-23, 10, buffer));
assertEquals("", buffer.getRawDigits());
assertEquals(-10, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1e-123, 2, buffer));
assertEquals("", buffer.getRawDigits());
assertEquals(-2, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1e-123, 0, buffer));
assertEquals("", buffer.getRawDigits());
assertEquals(0, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1e-23, 20, buffer));
assertEquals("", buffer.getRawDigits());
assertEquals(-20, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1e-21, 20, buffer));
assertEquals("", buffer.getRawDigits());
assertEquals(-20, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1e-22, 20, buffer));
assertEquals("", buffer.getRawDigits());
assertEquals(-20, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(6e-21, 20, buffer));
assertEquals("1", buffer.getRawDigits());
assertEquals(-19, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(9.1193616301674545152000000e+19, 0, buffer));
assertEquals("91193616301674545152", buffer.getRawDigits());
assertEquals(20, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(4.8184662102767651659096515e-04, 19, buffer));
assertEquals("4818466210276765", buffer.getRawDigits());
assertEquals(-3, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1.9023164229540652612705182e-23, 8, buffer));
assertEquals("", buffer.getRawDigits());
assertEquals(-8, buffer.getDecimalPoint());
buffer.reset();
assertTrue(DoubleConversion.fixedDtoa(1000000000000000128.0, 0, buffer));
assertEquals("1000000000000000128", buffer.getRawDigits());
assertEquals(19, buffer.getDecimalPoint());
buffer.reset();
}
@Test
public void testFastFixed() {
final AtomicInteger total = new AtomicInteger();
final AtomicInteger succeeded = new AtomicInteger();
new BufferedReader(new InputStreamReader(getClass().getResourceAsStream("resources/gay-fixed.txt")))
.lines()
.forEach(line -> {
if (line.isEmpty() || line.startsWith("//")) {
return; // comment or empty line
}
final String[] tokens = line.split(",\\s+");
assertEquals(tokens.length, 4);
final double v = Double.parseDouble(tokens[0]);
final int digits = Integer.parseInt(tokens[1]);
final String str = tokens[2].replace('"', ' ').trim();;
final int point = Integer.parseInt(tokens[3]);
final DtoaBuffer buffer = new DtoaBuffer(kBufferSize);
total.getAndIncrement();
if (DoubleConversion.fixedDtoa(v, digits, buffer)) {
assertEquals(str, buffer.getRawDigits());
assertEquals(point, buffer.getDecimalPoint());
succeeded.getAndIncrement();
}
});
// should work for all numbers
assertEquals(succeeded.get(), total.get());
}
}

@ -0,0 +1,332 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
// This file is available under and governed by the GNU General Public
// License version 2 only, as published by the Free Software Foundation.
// However, the following notice accompanied the original version of this
// file:
//
// Copyright 2006-2008 the V8 project authors. All rights reserved.
package jdk.nashorn.internal.runtime.doubleconv.test;
import org.testng.annotations.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
/**
* Ieee class tests
*
* @test
* @run testng jdk.nashorn.internal.runtime.doubleconv.test.IeeeTest
*/
@SuppressWarnings({"unchecked", "javadoc"})
public class IeeeDoubleTest {
static final Method asDiyFp;
static final Method asNormalizedDiyFp;
static final Method doubleToLong;
static final Method longToDouble;
static final Method isDenormal;
static final Method isSpecial;
static final Method isInfinite;
static final Method isNaN;
static final Method value;
static final Method sign;
static final Method nextDouble;
static final Method previousDouble;
static final Method normalizedBoundaries;
static final Method Infinity;
static final Method NaN;
static final Method f;
static final Method e;
static final Constructor<?> DiyFpCtor;
static {
try {
final Class<?> IeeeDouble = Class.forName("jdk.nashorn.internal.runtime.doubleconv.IeeeDouble");
final Class DiyFp = Class.forName("jdk.nashorn.internal.runtime.doubleconv.DiyFp");
asDiyFp = method(IeeeDouble, "asDiyFp", long.class);
asNormalizedDiyFp = method(IeeeDouble, "asNormalizedDiyFp", long.class);
doubleToLong = method(IeeeDouble, "doubleToLong", double.class);
longToDouble = method(IeeeDouble, "longToDouble", long.class);
isDenormal = method(IeeeDouble, "isDenormal", long.class);
isSpecial = method(IeeeDouble, "isSpecial", long.class);
isInfinite = method(IeeeDouble, "isInfinite", long.class);
isNaN = method(IeeeDouble, "isNaN", long.class);
value = method(IeeeDouble, "value", long.class);
sign = method(IeeeDouble, "sign", long.class);
nextDouble = method(IeeeDouble, "nextDouble", long.class);
previousDouble = method(IeeeDouble, "previousDouble", long.class);
Infinity = method(IeeeDouble, "Infinity");
NaN = method(IeeeDouble, "NaN");
normalizedBoundaries = method(IeeeDouble, "normalizedBoundaries", long.class, DiyFp, DiyFp);
DiyFpCtor = DiyFp.getDeclaredConstructor();
DiyFpCtor.setAccessible(true);
f = method(DiyFp, "f");
e = method(DiyFp, "e");
} catch (final Exception e) {
throw new RuntimeException(e);
}
}
private static Method method(final Class<?> clazz, final String name, final Class<?>... params) throws NoSuchMethodException {
final Method m = clazz.getDeclaredMethod(name, params);
m.setAccessible(true);
return m;
}
@Test
public void testUint64Conversions() throws Exception {
// Start by checking the byte-order.
final long ordered = 0x0123456789ABCDEFL;
assertEquals(3512700564088504e-318, value.invoke(null, ordered));
final long min_double64 = 0x0000000000000001L;
assertEquals(5e-324, value.invoke(null, min_double64));
final long max_double64 = 0x7fefffffffffffffL;
assertEquals(1.7976931348623157e308, value.invoke(null, max_double64));
}
@Test
public void testDoubleAsDiyFp() throws Exception {
final long ordered = 0x0123456789ABCDEFL;
Object diy_fp = asDiyFp.invoke(null, ordered);
assertEquals(0x12 - 0x3FF - 52, e.invoke(diy_fp));
// The 52 mantissa bits, plus the implicit 1 in bit 52 as a UINT64.
assertTrue(0x0013456789ABCDEFL == (long) f.invoke(diy_fp));
final long min_double64 = 0x0000000000000001L;
diy_fp = asDiyFp.invoke(null, min_double64);
assertEquals(-0x3FF - 52 + 1, e.invoke(diy_fp));
// This is a denormal; so no hidden bit.
assertTrue(1L == (long) f.invoke(diy_fp));
final long max_double64 = 0x7fefffffffffffffL;
diy_fp = asDiyFp.invoke(null, max_double64);
assertEquals(0x7FE - 0x3FF - 52, e.invoke(diy_fp));
assertTrue(0x001fffffffffffffL == (long) f.invoke(diy_fp));
}
@Test
public void testAsNormalizedDiyFp() throws Exception {
final long ordered = 0x0123456789ABCDEFL;
Object diy_fp = asNormalizedDiyFp.invoke(null, ordered);
assertEquals(0x12 - 0x3FF - 52 - 11, (int) e.invoke(diy_fp));
assertTrue((0x0013456789ABCDEFL << 11) == (long) f.invoke(diy_fp));
final long min_double64 = 0x0000000000000001L;
diy_fp = asNormalizedDiyFp.invoke(null, min_double64);
assertEquals(-0x3FF - 52 + 1 - 63, e.invoke(diy_fp));
// This is a denormal; so no hidden bit.
assertTrue(0x8000000000000000L == (long) f.invoke(diy_fp));
final long max_double64 = 0x7fefffffffffffffL;
diy_fp = asNormalizedDiyFp.invoke(null, max_double64);
assertEquals(0x7FE - 0x3FF - 52 - 11, e.invoke(diy_fp));
assertTrue((0x001fffffffffffffL << 11) == (long) f.invoke(diy_fp));
}
@Test
public void testIsDenormal() throws Exception {
final long min_double64 = 0x0000000000000001L;
assertTrue((boolean) isDenormal.invoke(null, min_double64));
long bits = 0x000FFFFFFFFFFFFFL;
assertTrue((boolean) isDenormal.invoke(null, bits));
bits = 0x0010000000000000L;
assertTrue(!(boolean) isDenormal.invoke(null, bits));
}
@Test
public void testIsSpecial() throws Exception {
assertTrue((boolean) isSpecial.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
assertTrue((boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
assertTrue((boolean) isSpecial.invoke(null, doubleToLong.invoke(null, NaN.invoke(null))));
final long bits = 0xFFF1234500000000L;
assertTrue((boolean) isSpecial.invoke(null, bits));
// Denormals are not special:
assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 5e-324)));
assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -5e-324)));
// And some random numbers:
assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 0.0)));
assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -0.0)));
assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 1.0)));
assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -1.0)));
assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 1000000.0)));
assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -1000000.0)));
assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 1e23)));
assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -1e23)));
assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, 1.7976931348623157e308)));
assertTrue(!(boolean) isSpecial.invoke(null, doubleToLong.invoke(null, -1.7976931348623157e308)));
}
@Test
public void testIsInfinite() throws Exception {
assertTrue((boolean) isInfinite.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
assertTrue((boolean) isInfinite.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, NaN.invoke(null))));
assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, 0.0)));
assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, -0.0)));
assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, 1.0)));
assertTrue(!(boolean) isInfinite.invoke(null, doubleToLong.invoke(null, -1.0)));
final long min_double64 = 0x0000000000000001L;
assertTrue(!(boolean) isInfinite.invoke(null, min_double64));
}
@Test
public void testIsNan() throws Exception {
assertTrue((boolean) isNaN.invoke(null, doubleToLong.invoke(null, NaN.invoke(null))));
final long other_nan = 0xFFFFFFFF00000001L;
assertTrue((boolean) isNaN.invoke(null, other_nan));
assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, 0.0)));
assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, -0.0)));
assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, 1.0)));
assertTrue(!(boolean) isNaN.invoke(null, doubleToLong.invoke(null, -1.0)));
final long min_double64 = 0x0000000000000001L;
assertTrue(!(boolean) isNaN.invoke(null, min_double64));
}
@Test
public void testSign() throws Exception {
assertEquals(1, (int) sign.invoke(null, doubleToLong.invoke(null, 1.0)));
assertEquals(1, (int) sign.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
assertEquals(-1, (int) sign.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
assertEquals(1, (int) sign.invoke(null, doubleToLong.invoke(null, 0.0)));
assertEquals(-1, (int) sign.invoke(null, doubleToLong.invoke(null, -0.0)));
final long min_double64 = 0x0000000000000001L;
assertEquals(1, (int) sign.invoke(null, min_double64));
}
@Test
public void testNormalizedBoundaries() throws Exception {
Object boundary_plus = DiyFpCtor.newInstance();
Object boundary_minus = DiyFpCtor.newInstance();
Object diy_fp = asNormalizedDiyFp.invoke(null, doubleToLong.invoke(null, 1.5));
normalizedBoundaries.invoke(null, doubleToLong.invoke(null, 1.5), boundary_minus, boundary_plus);
assertEquals(e.invoke(diy_fp), e.invoke(boundary_minus));
assertEquals(e.invoke(diy_fp), e.invoke(boundary_plus));
// 1.5 does not have a significand of the form 2^p (for some p).
// Therefore its boundaries are at the same distance.
assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
assertTrue((1 << 10) == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
diy_fp =asNormalizedDiyFp.invoke(null, doubleToLong.invoke(null, 1.0));
normalizedBoundaries.invoke(null, doubleToLong.invoke(null, 1.0), boundary_minus, boundary_plus);
assertEquals(e.invoke(diy_fp), e.invoke(boundary_minus));
assertEquals(e.invoke(diy_fp), e.invoke(boundary_plus));
// 1.0 does have a significand of the form 2^p (for some p).
// Therefore its lower boundary is twice as close as the upper boundary.
assertTrue((long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp) > (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
assertTrue((1L << 9) == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
assertTrue((1L << 10) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
final long min_double64 = 0x0000000000000001L;
diy_fp = asNormalizedDiyFp.invoke(null, min_double64);
normalizedBoundaries.invoke(null, min_double64, boundary_minus, boundary_plus);
assertEquals(e.invoke(diy_fp), e.invoke(boundary_minus));
assertEquals(e.invoke(diy_fp), e.invoke(boundary_plus));
// min-value does not have a significand of the form 2^p (for some p).
// Therefore its boundaries are at the same distance.
assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
// Denormals have their boundaries much closer.
assertTrue(1L << 62 == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
final long smallest_normal64 = 0x0010000000000000L;
diy_fp = asNormalizedDiyFp.invoke(null, smallest_normal64);
normalizedBoundaries.invoke(null, smallest_normal64, boundary_minus, boundary_plus);
assertEquals(e.invoke(diy_fp), e.invoke(boundary_minus));
assertEquals(e.invoke(diy_fp), e.invoke(boundary_plus));
// Even though the significand is of the form 2^p (for some p), its boundaries
// are at the same distance. (This is the only exception).
assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
assertTrue(1L << 10 == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
final long largest_denormal64 = 0x000FFFFFFFFFFFFFL;
diy_fp = asNormalizedDiyFp.invoke(null, largest_denormal64);
normalizedBoundaries.invoke(null, largest_denormal64, boundary_minus, boundary_plus);
assertEquals(e.invoke(diy_fp), e.invoke(boundary_minus));
assertEquals(e.invoke(diy_fp), e.invoke(boundary_plus));
assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
assertTrue(1L << 11 == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
final long max_double64 = 0x7fefffffffffffffL;
diy_fp = asNormalizedDiyFp.invoke(null, max_double64);
normalizedBoundaries.invoke(null, max_double64, boundary_minus, boundary_plus);
assertEquals(e.invoke(diy_fp), e.invoke(boundary_minus));
assertEquals(e.invoke(diy_fp), e.invoke(boundary_plus));
// max-value does not have a significand of the form 2^p (for some p).
// Therefore its boundaries are at the same distance.
assertTrue((long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus) == (long) f.invoke(boundary_plus) - (long) f.invoke(diy_fp));
assertTrue(1L << 10 == (long) f.invoke(diy_fp) - (long) f.invoke(boundary_minus));
}
@Test
public void testNextDouble() throws Exception {
assertEquals(4e-324, (double) nextDouble.invoke(null, doubleToLong.invoke(null, 0.0)));
assertEquals(0.0, (double) nextDouble.invoke(null, doubleToLong.invoke(null, -0.0)));
assertEquals(-0.0, (double) nextDouble.invoke(null, doubleToLong.invoke(null, -4e-324)));
assertTrue((int) sign.invoke(null, doubleToLong.invoke(null, nextDouble.invoke(null, doubleToLong.invoke(null, -0.0)))) > 0);
assertTrue((int) sign.invoke(null, doubleToLong.invoke(null, nextDouble.invoke(null, doubleToLong.invoke(null, -4e-324)))) < 0);
final long d0 = (long) doubleToLong.invoke(null, -4e-324);
final long d1 = (long) doubleToLong.invoke(null, nextDouble.invoke(null, d0));
final long d2 = (long) doubleToLong.invoke(null, nextDouble.invoke(null, d1));
assertEquals(-0.0, value.invoke(null, d1));
assertTrue((int) sign.invoke(null, d1) < 0);
assertEquals(0.0, value.invoke(null, d2));
assertTrue((int) sign.invoke(null, d2) > 0);
assertEquals(4e-324, (double) nextDouble.invoke(null, d2));
assertEquals(-1.7976931348623157e308, (double) nextDouble.invoke(null, doubleToLong.invoke(null, -(double) Infinity.invoke(null))));
assertEquals(Infinity.invoke(null), (double) nextDouble.invoke(null, 0x7fefffffffffffffL));
}
@Test
public void testPreviousDouble() throws Exception {
assertEquals(0.0, (double) previousDouble.invoke(null, doubleToLong.invoke(null, 4e-324)));
assertEquals(-0.0, (double) previousDouble.invoke(null, doubleToLong.invoke(null, 0.0)));
assertTrue((int) sign.invoke(null, doubleToLong.invoke(null, previousDouble.invoke(null, doubleToLong.invoke(null, 0.0)))) < 0);
assertEquals(-4e-324, previousDouble.invoke(null, doubleToLong.invoke(null, -0.0)));
final long d0 = (long) doubleToLong.invoke(null, 4e-324);
final long d1 = (long) doubleToLong.invoke(null, previousDouble.invoke(null, d0));
final long d2 = (long) doubleToLong.invoke(null, previousDouble.invoke(null, d1));
assertEquals(0.0, value.invoke(null, d1));
assertTrue((int) sign.invoke(null, d1) > 0);
assertEquals(-0.0, value.invoke(null, d2));
assertTrue((int) sign.invoke(null, d2) < 0);
assertEquals(-4e-324, (double) previousDouble.invoke(null, d2));
assertEquals(1.7976931348623157e308, (double) previousDouble.invoke(null, doubleToLong.invoke(null, Infinity.invoke(null))));
assertEquals(-(double) Infinity.invoke(null), (double) previousDouble.invoke(null, 0xffefffffffffffffL));
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff