8010803: Number to String conversion functionality overhaul
Reviewed-by: attila, lagergren
This commit is contained in:
parent
534ff3deff
commit
15ef19ee62
nashorn
make
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal
objects
runtime
test
script/basic
src/jdk/nashorn/internal/runtime/doubleconv/test
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
786
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/NumberToString.java
786
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/NumberToString.java
@ -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();
|
||||
}
|
||||
}
|
842
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/Bignum.java
Normal file
842
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/Bignum.java
Normal file
@ -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_);
|
||||
}
|
||||
}
|
647
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/BignumDtoa.java
Normal file
647
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/BignumDtoa.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
204
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/CachedPowers.java
Normal file
204
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/CachedPowers.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
172
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DiyFp.java
Normal file
172
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DiyFp.java
Normal file
@ -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_ + "]";
|
||||
}
|
||||
|
||||
}
|
214
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DoubleConversion.java
Normal file
214
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DoubleConversion.java
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
223
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DtoaBuffer.java
Normal file
223
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DtoaBuffer.java
Normal file
@ -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 + "]";
|
||||
}
|
||||
}
|
82
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DtoaMode.java
Normal file
82
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/DtoaMode.java
Normal file
@ -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
|
||||
}
|
651
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/FastDtoa.java
Normal file
651
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/FastDtoa.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
425
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/FixedDtoa.java
Normal file
425
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/FixedDtoa.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
233
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/IeeeDouble.java
Normal file
233
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/doubleconv/IeeeDouble.java
Normal file
@ -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));
|
||||
}
|
||||
|
||||
}
|
100059
nashorn/test/src/jdk/nashorn/internal/runtime/doubleconv/test/resources/gay-fixed.txt
Normal file
100059
nashorn/test/src/jdk/nashorn/internal/runtime/doubleconv/test/resources/gay-fixed.txt
Normal file
File diff suppressed because it is too large
Load Diff
100059
nashorn/test/src/jdk/nashorn/internal/runtime/doubleconv/test/resources/gay-precision.txt
Normal file
100059
nashorn/test/src/jdk/nashorn/internal/runtime/doubleconv/test/resources/gay-precision.txt
Normal file
File diff suppressed because it is too large
Load Diff
100059
nashorn/test/src/jdk/nashorn/internal/runtime/doubleconv/test/resources/gay-shortest.txt
Normal file
100059
nashorn/test/src/jdk/nashorn/internal/runtime/doubleconv/test/resources/gay-shortest.txt
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user