diff --git a/src/java.base/share/classes/java/text/DigitList.java b/src/java.base/share/classes/java/text/DigitList.java
index 9ad1203fb62..140a5827860 100644
--- a/src/java.base/share/classes/java/text/DigitList.java
+++ b/src/java.base/share/classes/java/text/DigitList.java
@@ -322,7 +322,7 @@ final class DigitList implements Cloneable {
void set(boolean isNegative, double source, int maximumDigits, boolean fixedPoint) {
assert Double.isFinite(source);
- FloatingDecimal.BinaryToASCIIConverter fdConverter =
+ FloatingDecimal.BinaryToASCIIBuffer fdConverter =
FloatingDecimal.getBinaryToASCIIConverter(source, COMPAT);
boolean hasBeenRoundedUp = fdConverter.digitsRoundedUp();
boolean valueExactAsDecimal = fdConverter.decimalDigitsExact();
diff --git a/src/java.base/share/classes/jdk/internal/math/FDBigInteger.java b/src/java.base/share/classes/jdk/internal/math/FDBigInteger.java
index 90113a4a271..1ef9dee3a8a 100644
--- a/src/java.base/share/classes/jdk/internal/math/FDBigInteger.java
+++ b/src/java.base/share/classes/jdk/internal/math/FDBigInteger.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2025, 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
@@ -22,62 +22,31 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package jdk.internal.math;
import jdk.internal.misc.CDS;
+import jdk.internal.vm.annotation.Stable;
-import java.math.BigInteger;
import java.util.Arrays;
-//@ model import org.jmlspecs.models.JMLMath;
/**
- * A simple big integer package specifically for floating point base conversion.
+ * A simple big integer class specifically for floating point base conversion.
*/
-public /*@ spec_bigint_math @*/ class FDBigInteger {
-
- //
- // This class contains many comments that start with "/*@" mark.
- // They are behavourial specification in
- // the Java Modelling Language (JML):
- // http://www.eecs.ucf.edu/~leavens/JML//index.shtml
- //
-
- /*@
- @ public pure model static \bigint UNSIGNED(int v) {
- @ return v >= 0 ? v : v + (((\bigint)1) << 32);
- @ }
- @
- @ public pure model static \bigint UNSIGNED(long v) {
- @ return v >= 0 ? v : v + (((\bigint)1) << 64);
- @ }
- @
- @ public pure model static \bigint AP(int[] data, int len) {
- @ return (\sum int i; 0 <= 0 && i < len; UNSIGNED(data[i]) << (i*32));
- @ }
- @
- @ public pure model static \bigint pow52(int p5, int p2) {
- @ ghost \bigint v = 1;
- @ for (int i = 0; i < p5; i++) v *= 5;
- @ return v << p2;
- @ }
- @
- @ public pure model static \bigint pow10(int p10) {
- @ return pow52(p10, p10);
- @ }
- @*/
+final class FDBigInteger {
+ @Stable
static final int[] SMALL_5_POW;
+ @Stable
static final long[] LONG_5_POW;
- // Maximum size of cache of powers of 5 as FDBigIntegers.
+ // Size of full cache of powers of 5 as FDBigIntegers.
private static final int MAX_FIVE_POW = 340;
// Cache of big powers of 5 as FDBigIntegers.
- private static final FDBigInteger POW_5_CACHE[];
-
- // Zero as an FDBigInteger.
- public static final FDBigInteger ZERO;
+ @Stable
+ private static final FDBigInteger[] POW_5_CACHE;
// Archive proxy
private static Object[] archivedCaches;
@@ -87,114 +56,63 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
CDS.initializeFromArchive(FDBigInteger.class);
Object[] caches = archivedCaches;
if (caches == null) {
- long[] long5pow = {
- 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,
- };
- int[] small5pow = {
- 1,
- 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 * 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 * 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 * 5 * 5 * 5,
- 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5
- };
+ int[] small5pow = new int[13 + 1]; // 5^13 fits in an int, 5^14 does not
+ small5pow[0] = 1;
+ for (int i = 1; i < small5pow.length; ++i) {
+ small5pow[i] = 5 * small5pow[i - 1];
+ }
+
+ long[] long5pow = new long[27 + 1]; // 5^27 fits in a long, 5^28 does not
+ long5pow[0] = 1;
+ for (int i = 1; i < long5pow.length; ++i) {
+ long5pow[i] = 5 * long5pow[i - 1];
+ }
+
FDBigInteger[] pow5cache = new FDBigInteger[MAX_FIVE_POW];
int i = 0;
- while (i < small5pow.length) {
- FDBigInteger pow5 = new FDBigInteger(new int[] { small5pow[i] }, 0);
- pow5.makeImmutable();
- pow5cache[i] = pow5;
- i++;
+ for (; i < long5pow.length; ++i) {
+ pow5cache[i] = new FDBigInteger(long5pow[i]).makeImmutable();
}
FDBigInteger prev = pow5cache[i - 1];
- while (i < MAX_FIVE_POW) {
- pow5cache[i] = prev = prev.mult(5);
- prev.makeImmutable();
- i++;
+ for (; i < MAX_FIVE_POW; ++i) {
+ pow5cache[i] = prev = prev.mult(5).makeImmutable();
}
- FDBigInteger zero = new FDBigInteger(new int[0], 0);
- zero.makeImmutable();
- archivedCaches = caches = new Object[] {small5pow, long5pow, pow5cache, zero};
+
+ /* Here prev is 5^(MAX_FIVE_POW-1). */
+ FDBigInteger[] largePow5cache =
+ new FDBigInteger[(2 - DoubleToDecimal.Q_MIN) - MAX_FIVE_POW + 1];
+ largePow5cache[2 * (MAX_FIVE_POW - 1) - MAX_FIVE_POW] =
+ prev = prev.mult(prev).makeImmutable();
+ largePow5cache[3 * (MAX_FIVE_POW - 1) - MAX_FIVE_POW] =
+ pow5cache[MAX_FIVE_POW - 1].mult(prev).makeImmutable();
+ archivedCaches = caches = new Object[] {small5pow, long5pow, pow5cache, largePow5cache};
}
- SMALL_5_POW = (int[])caches[0];
- LONG_5_POW = (long[])caches[1];
- POW_5_CACHE = (FDBigInteger[])caches[2];
- ZERO = (FDBigInteger)caches[3];
+ SMALL_5_POW = (int[]) caches[0];
+ LONG_5_POW = (long[]) caches[1];
+ POW_5_CACHE = (FDBigInteger[]) caches[2];
+ LARGE_POW_5_CACHE = (FDBigInteger[]) caches[3];
}
// Constant for casting an int to a long via bitwise AND.
- private static final long LONG_MASK = 0xffffffffL;
+ private static final long LONG_MASK = 0xffff_ffffL;
- //@ spec_public non_null;
- private int data[]; // value: data[0] is least significant
- //@ spec_public;
+ private int[] data; // value: data[0] is least significant
private int offset; // number of least significant zero padding ints
- //@ spec_public;
private int nWords; // data[nWords-1]!=0, all values above are zero
// if nWords==0 -> this FDBigInteger is zero
- //@ spec_public;
private boolean isImmutable = false;
- /*@
- @ public invariant 0 <= nWords && nWords <= data.length && offset >= 0;
- @ public invariant nWords == 0 ==> offset == 0;
- @ public invariant nWords > 0 ==> data[nWords - 1] != 0;
- @ public invariant (\forall int i; nWords <= i && i < data.length; data[i] == 0);
- @ public pure model \bigint value() {
- @ return AP(data, nWords) << (offset*32);
- @ }
- @*/
-
/**
- * Constructs an FDBigInteger from data and padding. The
- * data parameter has the least significant int at
- * the zeroth index. The offset parameter gives the number of
- * zero ints to be inferred below the least significant element
- * of data.
+ * Constructs an {@link FDBigInteger} from data and padding. The
+ * {@code data} parameter has the least significant {@code int} at
+ * the zeroth index. The {@code offset} parameter gives the number of
+ * zero {@code int}s to be inferred below the least significant element
+ * of {@code data}.
*
- * @param data An array containing all non-zero ints of the value.
- * @param offset An offset indicating the number of zero ints to pad
- * below the least significant element of data.
+ * @param data An array containing all non-zero {@code int}s of the value.
+ * @param offset An offset indicating the number of zero {@code int}s to pad
+ * below the least significant element of {@code data}.
*/
- /*@
- @ requires data != null && offset >= 0;
- @ ensures this.value() == \old(AP(data, data.length) << (offset*32));
- @ ensures this.data == \old(data);
- @*/
private FDBigInteger(int[] data, int offset) {
this.data = data;
this.offset = offset;
@@ -203,198 +121,156 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
}
/**
- * Constructs an FDBigInteger from a starting value and some
+ * Constructs an {@link FDBigInteger} from a starting value and some
* decimal digits.
*
* @param lValue The starting value.
* @param digits The decimal digits.
- * @param kDigits The initial index into digits.
- * @param nDigits The final index into digits.
+ * @param i The initial index into {@code digits}.
+ * @param nDigits The final index into {@code digits}.
*/
- /*@
- @ requires digits != null;
- @ requires 0 <= kDigits && kDigits <= nDigits && nDigits <= digits.length;
- @ requires (\forall int i; 0 <= i && i < nDigits; '0' <= digits[i] && digits[i] <= '9');
- @ ensures this.value() == \old(lValue * pow10(nDigits - kDigits) + (\sum int i; kDigits <= i && i < nDigits; (digits[i] - '0') * pow10(nDigits - i - 1)));
- @*/
- public FDBigInteger(long lValue, byte[] digits, int kDigits, int nDigits) {
- int n = Math.max((nDigits + 8) / 9, 2); // estimate size needed.
- data = new int[n]; // allocate enough space
- data[0] = (int) lValue; // starting value
+ public FDBigInteger(long lValue, byte[] digits, int i, int nDigits) {
+ int n = (nDigits + 8) / 9; // estimate size needed: ⌈nDigits / 9⌉
+ data = new int[Math.max(n, 2)];
+ data[0] = (int) lValue; // starting value
data[1] = (int) (lValue >>> 32);
offset = 0;
nWords = 2;
- int i = kDigits;
- int limit = nDigits - 5; // slurp digits 5 at a time.
- int v;
+ int limit = nDigits - 9;
while (i < limit) {
- int ilim = i + 5;
- v = (int) digits[i++] - (int) '0';
+ int v = 0;
+ int ilim = i + 9;
while (i < ilim) {
- v = 10 * v + (int) digits[i++] - (int) '0';
+ v = 10 * v + digits[i++] - '0';
}
- multAddMe(100000, v); // ... where 100000 is 10^5.
+ multAdd(1_000_000_000, v); // 10^9
}
- int factor = 1;
- v = 0;
- while (i < nDigits) {
- v = 10 * v + (int) digits[i++] - (int) '0';
- factor *= 10;
- }
- if (factor != 1) {
- multAddMe(factor, v);
+ if (i < nDigits) {
+ int factor = (int) MathUtils.pow10(nDigits - i);
+ int v = 0;
+ while (i < nDigits) {
+ v = 10 * v + digits[i++] - '0';
+ }
+ multAdd(factor, v);
}
trimLeadingZeros();
}
- /**
- * Returns an FDBigInteger with the numerical value
- * 5p5 * 2p2.
- *
- * @param p5 The exponent of the power-of-five factor.
- * @param p2 The exponent of the power-of-two factor.
- * @return 5p5 * 2p2
- */
- /*@
- @ requires p5 >= 0 && p2 >= 0;
- @ assignable \nothing;
- @ ensures \result.value() == \old(pow52(p5, p2));
- @*/
- public static FDBigInteger valueOfPow52(int p5, int p2) {
- if (p5 != 0) {
- if (p2 == 0) {
- return big5pow(p5);
- } else if (p5 < SMALL_5_POW.length) {
- int pow5 = SMALL_5_POW[p5];
- int wordcount = p2 >> 5;
- int bitcount = p2 & 0x1f;
- if (bitcount == 0) {
- return new FDBigInteger(new int[]{pow5}, wordcount);
- } else {
- return new FDBigInteger(new int[]{
- pow5 << bitcount,
- pow5 >>> (32 - bitcount)
- }, wordcount);
- }
- } else {
- return big5pow(p5).leftShift(p2);
- }
- } else {
- return valueOfPow2(p2);
- }
+ public FDBigInteger(long v) {
+ this(new int[] {(int) v, (int) (v >>> 32)}, 0);
}
/**
- * Returns an FDBigInteger with the numerical value
- * value * 5p5 * 2p2.
+ * Returns an {@link FDBigInteger} with the numerical value
+ * 5{@code e5} * 2{@code e2}.
+ *
+ * @param e5 The exponent of the power-of-five factor.
+ * @param e2 The exponent of the power-of-two factor.
+ * @return 5{@code e5} * 2{@code e2}
+ */
+ public static FDBigInteger valueOfPow52(int e5, int e2) {
+ if (e5 == 0) {
+ return valueOfPow2(e2);
+ }
+ if (e2 == 0) {
+ return pow5(e5);
+ }
+ if (e5 >= SMALL_5_POW.length) {
+ return pow5(e5).leftShift(e2);
+ }
+ int pow5 = SMALL_5_POW[e5];
+ int offset = e2 >> 5;
+ int bitcount = e2 & 0x1f;
+ if (bitcount == 0) {
+ return new FDBigInteger(new int[] {pow5}, offset);
+ }
+ return new FDBigInteger(new int[] {
+ pow5 << bitcount,
+ pow5 >>> -bitcount
+ }, offset);
+ }
+
+ /**
+ * Returns an {@link FDBigInteger} with the numerical value:
+ * value * 5{@code e5} * 2{@code e2}.
*
* @param value The constant factor.
- * @param p5 The exponent of the power-of-five factor.
- * @param p2 The exponent of the power-of-two factor.
- * @return value * 5p5 * 2p2
+ * @param e5 The exponent of the power-of-five factor.
+ * @param e2 The exponent of the power-of-two factor.
+ * @return value * 5{@code e5} * 2{@code e2}
*/
- /*@
- @ requires p5 >= 0 && p2 >= 0;
- @ assignable \nothing;
- @ ensures \result.value() == \old(UNSIGNED(value) * pow52(p5, p2));
- @*/
- public static FDBigInteger valueOfMulPow52(long value, int p5, int p2) {
- assert p5 >= 0 : p5;
- assert p2 >= 0 : p2;
+ public static FDBigInteger valueOfMulPow52(long value, int e5, int e2) {
int v0 = (int) value;
int v1 = (int) (value >>> 32);
- int wordcount = p2 >> 5;
- int bitcount = p2 & 0x1f;
- if (p5 != 0) {
- if (p5 < SMALL_5_POW.length) {
- long pow5 = SMALL_5_POW[p5] & LONG_MASK;
+ int offset = e2 >> 5;
+ int bitcount = e2 & 0x1f;
+ if (e5 != 0) {
+ if (e5 < SMALL_5_POW.length) {
+ long pow5 = SMALL_5_POW[e5] & LONG_MASK;
long carry = (v0 & LONG_MASK) * pow5;
v0 = (int) carry;
carry >>>= 32;
carry = (v1 & LONG_MASK) * pow5 + carry;
v1 = (int) carry;
int v2 = (int) (carry >>> 32);
- if (bitcount == 0) {
- return new FDBigInteger(new int[]{v0, v1, v2}, wordcount);
- } else {
- return new FDBigInteger(new int[]{
- v0 << bitcount,
- (v1 << bitcount) | (v0 >>> (32 - bitcount)),
- (v2 << bitcount) | (v1 >>> (32 - bitcount)),
- v2 >>> (32 - bitcount)
- }, wordcount);
- }
- } else {
- FDBigInteger pow5 = big5pow(p5);
- int[] r;
- if (v1 == 0) {
- r = new int[pow5.nWords + 1 + ((p2 != 0) ? 1 : 0)];
- mult(pow5.data, pow5.nWords, v0, r);
- } else {
- r = new int[pow5.nWords + 2 + ((p2 != 0) ? 1 : 0)];
- mult(pow5.data, pow5.nWords, v0, v1, r);
- }
- return (new FDBigInteger(r, pow5.offset)).leftShift(p2);
+ return bitcount == 0
+ ? new FDBigInteger(new int[] {v0, v1, v2}, offset)
+ : new FDBigInteger(new int[] {
+ v0 << bitcount,
+ (v1 << bitcount) | (v0 >>> -bitcount),
+ (v2 << bitcount) | (v1 >>> -bitcount),
+ v2 >>> -bitcount
+ }, offset);
}
- } else if (p2 != 0) {
- if (bitcount == 0) {
- return new FDBigInteger(new int[]{v0, v1}, wordcount);
+ FDBigInteger pow5 = pow5(e5);
+ int[] r;
+ if (v1 == 0) {
+ r = new int[pow5.nWords + 1 + ((e2 != 0) ? 1 : 0)];
+ mult(pow5.data, pow5.nWords, v0, r);
} else {
- return new FDBigInteger(new int[]{
- v0 << bitcount,
- (v1 << bitcount) | (v0 >>> (32 - bitcount)),
- v1 >>> (32 - bitcount)
- }, wordcount);
+ r = new int[pow5.nWords + 2 + ((e2 != 0) ? 1 : 0)];
+ mult(pow5.data, pow5.nWords, v0, v1, r);
}
+ return (new FDBigInteger(r, 0)).leftShift(e2);
}
- return new FDBigInteger(new int[]{v0, v1}, 0);
+ if (e2 != 0) {
+ return bitcount == 0
+ ? new FDBigInteger(new int[] {v0, v1}, offset)
+ : new FDBigInteger(new int[] {
+ v0 << bitcount,
+ (v1 << bitcount) | (v0 >>> -bitcount),
+ v1 >>> -bitcount
+ }, offset);
+ }
+ return new FDBigInteger(new int[] {v0, v1}, 0);
}
/**
- * Returns an FDBigInteger with the numerical value
- * 2p2.
+ * Returns an {@link FDBigInteger} with the numerical value
+ * 2{@code e}.
*
- * @param p2 The exponent of 2.
- * @return 2p2
+ * @param e The exponent of 2.
+ * @return 2{@code e}
*/
- /*@
- @ requires p2 >= 0;
- @ assignable \nothing;
- @ ensures \result.value() == pow52(0, p2);
- @*/
- private static FDBigInteger valueOfPow2(int p2) {
- int wordcount = p2 >> 5;
- int bitcount = p2 & 0x1f;
- return new FDBigInteger(new int[]{1 << bitcount}, wordcount);
+ private static FDBigInteger valueOfPow2(int e) {
+ return new FDBigInteger(new int[] {1 << (e & 0x1f)}, e >> 5);
}
/**
- * Removes all leading zeros from this FDBigInteger adjusting
+ * Removes all leading zeros from this {@link FDBigInteger} adjusting
* the offset and number of non-zero leading words accordingly.
*/
- /*@
- @ requires data != null;
- @ requires 0 <= nWords && nWords <= data.length && offset >= 0;
- @ requires nWords == 0 ==> offset == 0;
- @ ensures nWords == 0 ==> offset == 0;
- @ ensures nWords > 0 ==> data[nWords - 1] != 0;
- @*/
- private /*@ helper @*/ void trimLeadingZeros() {
- int i = nWords;
- if (i > 0 && (data[--i] == 0)) {
- //for (; i > 0 && data[i - 1] == 0; i--) ;
- while(i > 0 && data[i - 1] == 0) {
- i--;
- }
- this.nWords = i;
- if (i == 0) { // all words are zero
- this.offset = 0;
- }
+ private void trimLeadingZeros() {
+ int i = nWords - 1;
+ for (; i >= 0 && data[i] == 0; --i); // empty body
+ nWords = i + 1;
+ if (i < 0) {
+ offset = 0;
}
}
/**
- * Retrieves the normalization bias of the FDBigIntger. The
+ * Retrieves the normalization bias of the {@link FDBigInteger}. The
* normalization bias is a left shift such that after it the highest word
* of the value will have the 4 highest bits equal to zero:
* {@code (highestWord & 0xf0000000) == 0}, but the next bit should be 1
@@ -402,10 +278,7 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
*
* @return The normalization bias.
*/
- /*@
- @ requires this.value() > 0;
- @*/
- public /*@ pure @*/ int getNormalizationBias() {
+ public int getNormalizationBias() {
if (nWords == 0) {
throw new IllegalArgumentException("Zero value cannot be normalized");
}
@@ -413,7 +286,6 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
return (zeros < 4) ? 28 + zeros : zeros - 4;
}
- // TODO: Why is anticount param needed if it is always 32 - bitcount?
/**
* Left shifts the contents of one int array into another.
*
@@ -421,20 +293,13 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
* @param idx The initial index of the source array.
* @param result The destination array.
* @param bitcount The left shift.
- * @param anticount The left anti-shift, e.g., 32-bitcount.
* @param prev The prior source value.
*/
- /*@
- @ requires 0 < bitcount && bitcount < 32 && anticount == 32 - bitcount;
- @ requires src.length >= idx && result.length > idx;
- @ assignable result[*];
- @ ensures AP(result, \old(idx + 1)) == \old((AP(src, idx) + UNSIGNED(prev) << (idx*32)) << bitcount);
- @*/
- private static void leftShift(int[] src, int idx, int result[], int bitcount, int anticount, int prev){
+ private static void leftShift(int[] src, int idx, int[] result, int bitcount, int prev) {
for (; idx > 0; idx--) {
- int v = (prev << bitcount);
+ int v = prev << bitcount;
prev = src[idx - 1];
- v |= (prev >>> anticount);
+ v |= prev >>> -bitcount;
result[idx] = v;
}
int v = prev << bitcount;
@@ -442,111 +307,88 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
}
/**
- * Shifts this FDBigInteger to the left. The shift is performed
- * in-place unless the FDBigInteger is immutable in which case
- * a new instance of FDBigInteger is returned.
+ * Shifts this {@link FDBigInteger} to the left. The shift is performed
+ * in-place unless the {@link FDBigInteger} is immutable in which case
+ * a new instance of {@link FDBigInteger} is returned.
*
* @param shift The number of bits to shift left.
- * @return The shifted FDBigInteger.
+ * @return The shifted {@link FDBigInteger}.
*/
- /*@
- @ requires this.value() == 0 || shift == 0;
- @ assignable \nothing;
- @ ensures \result == this;
- @
- @ also
- @
- @ requires this.value() > 0 && shift > 0 && this.isImmutable;
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() << shift);
- @
- @ also
- @
- @ requires this.value() > 0 && shift > 0 && this.isImmutable;
- @ assignable \nothing;
- @ ensures \result == this;
- @ ensures \result.value() == \old(this.value() << shift);
- @*/
public FDBigInteger leftShift(int shift) {
if (shift == 0 || nWords == 0) {
return this;
}
int wordcount = shift >> 5;
int bitcount = shift & 0x1f;
- if (this.isImmutable) {
+ if (isImmutable) {
if (bitcount == 0) {
return new FDBigInteger(Arrays.copyOf(data, nWords), offset + wordcount);
+ }
+ int idx = nWords - 1;
+ int prev = data[idx];
+ int hi = prev >>> -bitcount;
+ int[] result;
+ if (hi != 0) {
+ result = new int[nWords + 1];
+ result[nWords] = hi;
+ } else {
+ result = new int[nWords];
+ }
+ leftShift(data, idx, result, bitcount, prev);
+ return new FDBigInteger(result, offset + wordcount);
+ }
+ if (bitcount != 0) {
+ if (data[0] << bitcount == 0) {
+ int idx = 0;
+ int prev = data[idx];
+ for (; idx < nWords - 1; idx++) {
+ int v = prev >>> -bitcount;
+ prev = data[idx + 1];
+ v |= prev << bitcount;
+ data[idx] = v;
+ }
+ int v = prev >>> -bitcount;
+ data[idx] = v;
+ if (v == 0) {
+ nWords--;
+ }
+ offset++;
} else {
- int anticount = 32 - bitcount;
int idx = nWords - 1;
int prev = data[idx];
- int hi = prev >>> anticount;
- int[] result;
+ int hi = prev >>> -bitcount;
+ int[] result = data;
+ int[] src = data;
if (hi != 0) {
- result = new int[nWords + 1];
- result[nWords] = hi;
- } else {
- result = new int[nWords];
+ if (nWords == data.length) {
+ data = result = new int[nWords + 1];
+ }
+ result[nWords++] = hi;
}
- leftShift(data,idx,result,bitcount,anticount,prev);
- return new FDBigInteger(result, offset + wordcount);
+ leftShift(src, idx, result, bitcount, prev);
}
- } else {
- if (bitcount != 0) {
- int anticount = 32 - bitcount;
- if ((data[0] << bitcount) == 0) {
- int idx = 0;
- int prev = data[idx];
- for (; idx < nWords - 1; idx++) {
- int v = (prev >>> anticount);
- prev = data[idx + 1];
- v |= (prev << bitcount);
- data[idx] = v;
- }
- int v = prev >>> anticount;
- data[idx] = v;
- if(v==0) {
- nWords--;
- }
- offset++;
- } else {
- int idx = nWords - 1;
- int prev = data[idx];
- int hi = prev >>> anticount;
- int[] result = data;
- int[] src = data;
- if (hi != 0) {
- if(nWords == data.length) {
- data = result = new int[nWords + 1];
- }
- result[nWords++] = hi;
- }
- leftShift(src,idx,result,bitcount,anticount,prev);
- }
- }
- offset += wordcount;
- return this;
}
+ offset += wordcount;
+ return this;
}
/**
- * Returns the number of ints this FDBigInteger represents.
+ * Returns the number of {@code int}s this {@link FDBigInteger} represents.
*
- * @return Number of ints required to represent this FDBigInteger.
+ * @return Number of {@code int}s required to represent this {@link FDBigInteger}.
*/
- /*@
- @ requires this.value() == 0;
- @ ensures \result == 0;
- @
- @ also
- @
- @ requires this.value() > 0;
- @ ensures ((\bigint)1) << (\result - 1) <= this.value() && this.value() <= ((\bigint)1) << \result;
- @*/
- private /*@ pure @*/ int size() {
+ private int size() {
return nWords + offset;
}
+ /**
+ * Returns whether this {@link FDBigInteger} is zero.
+ *
+ * @return {@code this} is zero.
+ */
+ public boolean isZero() {
+ return nWords == 0;
+ }
/**
* Computes
@@ -561,18 +403,9 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
* Also assumed, of course, is that the result, q, can be expressed
* as an integer, {@code 0 <= q < 10}.
*
- * @param S The divisor of this FDBigInteger.
- * @return q = (int)(this / S).
+ * @param S The divisor of this {@link FDBigInteger}.
+ * @return {@code q = (int)(this / S)}.
*/
- /*@
- @ requires !this.isImmutable;
- @ requires this.size() <= S.size();
- @ requires this.data.length + this.offset >= S.size();
- @ requires S.value() >= ((\bigint)1) << (S.size()*32 - 4);
- @ assignable this.nWords, this.offset, this.data, this.data[*];
- @ ensures \result == \old(this.value() / S.value());
- @ ensures this.value() == \old(10 * (this.value() % S.value()));
- @*/
public int quoRemIteration(FDBigInteger S) throws IllegalArgumentException {
assert !this.isImmutable : "cannot modify immutable value";
// ensure that this and S have the same number of
@@ -597,7 +430,7 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
// right. If not, then we're only off by a little and
// will re-add.
long q = (this.data[this.nWords - 1] & LONG_MASK) / (S.data[S.nWords - 1] & LONG_MASK);
- long diff = multDiffMe(q, S);
+ long diff = multSub(q, S);
if (diff != 0L) {
//@ assert q != 0;
//@ assert this.offset == \old(Math.min(this.offset, S.offset));
@@ -639,30 +472,12 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
}
/**
- * Multiplies this FDBigInteger by 10. The operation will be
- * performed in place unless the FDBigInteger is immutable in
- * which case a new FDBigInteger will be returned.
+ * Multiplies this {@link FDBigInteger} by 10. The operation will be
+ * performed in place unless the {@link FDBigInteger} is immutable in
+ * which case a new {@link FDBigInteger} will be returned.
*
- * @return The FDBigInteger multiplied by 10.
+ * @return The {@link FDBigInteger} multiplied by 10.
*/
- /*@
- @ requires this.value() == 0;
- @ assignable \nothing;
- @ ensures \result == this;
- @
- @ also
- @
- @ requires this.value() > 0 && this.isImmutable;
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() * 10);
- @
- @ also
- @
- @ requires this.value() > 0 && !this.isImmutable;
- @ assignable this.nWords, this.data, this.data[*];
- @ ensures \result == this;
- @ ensures \result.value() == \old(this.value() * 10);
- @*/
public FDBigInteger multBy10() {
if (nWords == 0) {
return this;
@@ -691,71 +506,55 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
}
/**
- * Multiplies this FDBigInteger by
- * 5p5 * 2p2. The operation will be
- * performed in place if possible, otherwise a new FDBigInteger
+ * Multiplies this {@link FDBigInteger} by
+ * 5{@code e5} * 2{@code e2}. The operation will be
+ * performed in place if possible, otherwise a new {@link FDBigInteger}
* will be returned.
*
- * @param p5 The exponent of the power-of-five factor.
- * @param p2 The exponent of the power-of-two factor.
+ * @param e5 The exponent of the power-of-five factor.
+ * @param e2 The exponent of the power-of-two factor.
* @return The multiplication result.
*/
- /*@
- @ requires this.value() == 0 || p5 == 0 && p2 == 0;
- @ assignable \nothing;
- @ ensures \result == this;
- @
- @ also
- @
- @ requires this.value() > 0 && (p5 > 0 && p2 >= 0 || p5 == 0 && p2 > 0 && this.isImmutable);
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() * pow52(p5, p2));
- @
- @ also
- @
- @ requires this.value() > 0 && p5 == 0 && p2 > 0 && !this.isImmutable;
- @ assignable this.nWords, this.data, this.data[*];
- @ ensures \result == this;
- @ ensures \result.value() == \old(this.value() * pow52(p5, p2));
- @*/
- public FDBigInteger multByPow52(int p5, int p2) {
- if (this.nWords == 0) {
+ public FDBigInteger multByPow52(int e5, int e2) {
+ if (nWords == 0) {
return this;
}
FDBigInteger res = this;
- if (p5 != 0) {
+ if (e5 != 0) {
int[] r;
- int extraSize = (p2 != 0) ? 1 : 0;
- if (p5 < SMALL_5_POW.length) {
- r = new int[this.nWords + 1 + extraSize];
- mult(this.data, this.nWords, SMALL_5_POW[p5], r);
- res = new FDBigInteger(r, this.offset);
+ int extraSize = e2 != 0 ? 1 : 0; // accounts for e2 % 32 shift bits
+ if (e5 < SMALL_5_POW.length) {
+ r = new int[nWords + 1 + extraSize];
+ mult(data, nWords, SMALL_5_POW[e5], r);
+ } else if (e5 < LONG_5_POW.length) {
+ long pow5 = LONG_5_POW[e5];
+ r = new int[nWords + 2 + extraSize];
+ mult(data, nWords, (int) pow5, (int) (pow5 >>> 32), r);
} else {
- FDBigInteger pow5 = big5pow(p5);
- r = new int[this.nWords + pow5.size() + extraSize];
- mult(this.data, this.nWords, pow5.data, pow5.nWords, r);
- res = new FDBigInteger(r, this.offset + pow5.offset);
+ FDBigInteger pow5 = pow5(e5);
+ r = new int[nWords + pow5.nWords + extraSize];
+ mult(data, nWords, pow5.data, pow5.nWords, r);
}
+ res = new FDBigInteger(r, offset);
}
- return res.leftShift(p2);
+ return res.leftShift(e2);
}
/**
* Multiplies two big integers represented as int arrays.
*
* @param s1 The first array factor.
- * @param s1Len The number of elements of s1 to use.
+ * @param s1Len The number of elements of {@code s1} to use.
* @param s2 The second array factor.
- * @param s2Len The number of elements of s2 to use.
+ * @param s2Len The number of elements of {@code s2} to use.
* @param dst The product array.
*/
- /*@
- @ requires s1 != dst && s2 != dst;
- @ requires s1.length >= s1Len && s2.length >= s2Len && dst.length >= s1Len + s2Len;
- @ assignable dst[0 .. s1Len + s2Len - 1];
- @ ensures AP(dst, s1Len + s2Len) == \old(AP(s1, s1Len) * AP(s2, s2Len));
- @*/
private static void mult(int[] s1, int s1Len, int[] s2, int s2Len, int[] dst) {
+ if (s1Len > s2Len) {
+ /* Swap ensures that inner loop is longest. */
+ int l = s1Len; s1Len = s2Len; s2Len = l;
+ int[] s = s1; s1 = s2; s2 = s;
+ }
for (int i = 0; i < s1Len; i++) {
long v = s1[i] & LONG_MASK;
long p = 0L;
@@ -768,158 +567,6 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
}
}
- /**
- * Subtracts the supplied FDBigInteger subtrahend from this
- * FDBigInteger. Assert that the result is positive.
- * If the subtrahend is immutable, store the result in this(minuend).
- * If this(minuend) is immutable a new FDBigInteger is created.
- *
- * @param subtrahend The FDBigInteger to be subtracted.
- * @return This FDBigInteger less the subtrahend.
- */
- /*@
- @ requires this.isImmutable;
- @ requires this.value() >= subtrahend.value();
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() - subtrahend.value());
- @
- @ also
- @
- @ requires !subtrahend.isImmutable;
- @ requires this.value() >= subtrahend.value();
- @ assignable this.nWords, this.offset, this.data, this.data[*];
- @ ensures \result == this;
- @ ensures \result.value() == \old(this.value() - subtrahend.value());
- @*/
- public FDBigInteger leftInplaceSub(FDBigInteger subtrahend) {
- assert this.size() >= subtrahend.size() : "result should be positive";
- FDBigInteger minuend;
- if (this.isImmutable) {
- minuend = new FDBigInteger(this.data.clone(), this.offset);
- } else {
- minuend = this;
- }
- int offsetDiff = subtrahend.offset - minuend.offset;
- int[] sData = subtrahend.data;
- int[] mData = minuend.data;
- int subLen = subtrahend.nWords;
- int minLen = minuend.nWords;
- if (offsetDiff < 0) {
- // need to expand minuend
- int rLen = minLen - offsetDiff;
- if (rLen < mData.length) {
- System.arraycopy(mData, 0, mData, -offsetDiff, minLen);
- Arrays.fill(mData, 0, -offsetDiff, 0);
- } else {
- int[] r = new int[rLen];
- System.arraycopy(mData, 0, r, -offsetDiff, minLen);
- minuend.data = mData = r;
- }
- minuend.offset = subtrahend.offset;
- minuend.nWords = minLen = rLen;
- offsetDiff = 0;
- }
- long borrow = 0L;
- int mIndex = offsetDiff;
- for (int sIndex = 0; sIndex < subLen && mIndex < minLen; sIndex++, mIndex++) {
- long diff = (mData[mIndex] & LONG_MASK) - (sData[sIndex] & LONG_MASK) + borrow;
- mData[mIndex] = (int) diff;
- borrow = diff >> 32; // signed shift
- }
- for (; borrow != 0 && mIndex < minLen; mIndex++) {
- long diff = (mData[mIndex] & LONG_MASK) + borrow;
- mData[mIndex] = (int) diff;
- borrow = diff >> 32; // signed shift
- }
- assert borrow == 0L : borrow; // borrow out of subtract,
- // result should be positive
- minuend.trimLeadingZeros();
- return minuend;
- }
-
- /**
- * Subtracts the supplied FDBigInteger subtrahend from this
- * FDBigInteger. Assert that the result is positive.
- * If the this(minuend) is immutable, store the result in subtrahend.
- * If subtrahend is immutable a new FDBigInteger is created.
- *
- * @param subtrahend The FDBigInteger to be subtracted.
- * @return This FDBigInteger less the subtrahend.
- */
- /*@
- @ requires subtrahend.isImmutable;
- @ requires this.value() >= subtrahend.value();
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() - subtrahend.value());
- @
- @ also
- @
- @ requires !subtrahend.isImmutable;
- @ requires this.value() >= subtrahend.value();
- @ assignable subtrahend.nWords, subtrahend.offset, subtrahend.data, subtrahend.data[*];
- @ ensures \result == subtrahend;
- @ ensures \result.value() == \old(this.value() - subtrahend.value());
- @*/
- public FDBigInteger rightInplaceSub(FDBigInteger subtrahend) {
- assert this.size() >= subtrahend.size() : "result should be positive";
- FDBigInteger minuend = this;
- if (subtrahend.isImmutable) {
- subtrahend = new FDBigInteger(subtrahend.data.clone(), subtrahend.offset);
- }
- int offsetDiff = minuend.offset - subtrahend.offset;
- int[] sData = subtrahend.data;
- int[] mData = minuend.data;
- int subLen = subtrahend.nWords;
- int minLen = minuend.nWords;
- if (offsetDiff < 0) {
- int rLen = minLen;
- if (rLen < sData.length) {
- System.arraycopy(sData, 0, sData, -offsetDiff, subLen);
- Arrays.fill(sData, 0, -offsetDiff, 0);
- } else {
- int[] r = new int[rLen];
- System.arraycopy(sData, 0, r, -offsetDiff, subLen);
- subtrahend.data = sData = r;
- }
- subtrahend.offset = minuend.offset;
- subLen -= offsetDiff;
- offsetDiff = 0;
- } else {
- int rLen = minLen + offsetDiff;
- if (rLen >= sData.length) {
- subtrahend.data = sData = Arrays.copyOf(sData, rLen);
- }
- }
- //@ assert minuend == this && minuend.value() == \old(this.value());
- //@ assert mData == minuend.data && minLen == minuend.nWords;
- //@ assert subtrahend.offset + subtrahend.data.length >= minuend.size();
- //@ assert sData == subtrahend.data;
- //@ assert AP(subtrahend.data, subtrahend.data.length) << subtrahend.offset == \old(subtrahend.value());
- //@ assert subtrahend.offset == Math.min(\old(this.offset), minuend.offset);
- //@ assert offsetDiff == minuend.offset - subtrahend.offset;
- //@ assert 0 <= offsetDiff && offsetDiff + minLen <= sData.length;
- int sIndex = 0;
- long borrow = 0L;
- for (; sIndex < offsetDiff; sIndex++) {
- long diff = 0L - (sData[sIndex] & LONG_MASK) + borrow;
- sData[sIndex] = (int) diff;
- borrow = diff >> 32; // signed shift
- }
- //@ assert sIndex == offsetDiff;
- for (int mIndex = 0; mIndex < minLen; sIndex++, mIndex++) {
- //@ assert sIndex == offsetDiff + mIndex;
- long diff = (mData[mIndex] & LONG_MASK) - (sData[sIndex] & LONG_MASK) + borrow;
- sData[sIndex] = (int) diff;
- borrow = diff >> 32; // signed shift
- }
- assert borrow == 0L : borrow; // borrow out of subtract,
- // result should be positive
- subtrahend.nWords = sIndex;
- subtrahend.trimLeadingZeros();
- return subtrahend;
-
- }
-
/**
* Determines whether all elements of an array are zero for all indices less
* than a given index.
@@ -928,11 +575,7 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
* @param from The index strictly below which elements are to be examined.
* @return Zero if all elements in range are zero, 1 otherwise.
*/
- /*@
- @ requires 0 <= from && from <= a.length;
- @ ensures \result == (AP(a, from) == 0 ? 0 : 1);
- @*/
- private /*@ pure @*/ static int checkZeroTail(int[] a, int from) {
+ private static int checkZeroTail(int[] a, int from) {
while (from > 0) {
if (a[--from] != 0) {
return 1;
@@ -942,7 +585,7 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
}
/**
- * Compares the parameter with this FDBigInteger. Returns an
+ * Compares the parameter with this {@link FDBigInteger}. Returns an
* integer accordingly as:
*
{@code
* > 0: this > other
@@ -950,14 +593,11 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
* < 0: this < other
* }
*
- * @param other The FDBigInteger to compare.
+ * @param other The {@link FDBigInteger} to compare.
* @return A negative value, zero, or a positive value according to the
* result of the comparison.
*/
- /*@
- @ ensures \result == (this.value() < other.value() ? -1 : this.value() > other.value() ? +1 : 0);
- @*/
- public /*@ pure @*/ int cmp(FDBigInteger other) {
+ public int cmp(FDBigInteger other) {
int aSize = nWords + offset;
int bSize = other.nWords + other.offset;
if (aSize > bSize) {
@@ -968,10 +608,9 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
int aLen = nWords;
int bLen = other.nWords;
while (aLen > 0 && bLen > 0) {
- int a = data[--aLen];
- int b = other.data[--bLen];
- if (a != b) {
- return ((a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1;
+ int cmp = Integer.compareUnsigned(data[--aLen], other.data[--bLen]);
+ if (cmp != 0) {
+ return cmp;
}
}
if (aLen > 0) {
@@ -984,45 +623,7 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
}
/**
- * Compares this FDBigInteger with
- * 5p5 * 2p2.
- * Returns an integer accordingly as:
- * {@code
- * > 0: this > other
- * 0: this == other
- * < 0: this < other
- * }
- * @param p5 The exponent of the power-of-five factor.
- * @param p2 The exponent of the power-of-two factor.
- * @return A negative value, zero, or a positive value according to the
- * result of the comparison.
- */
- /*@
- @ requires p5 >= 0 && p2 >= 0;
- @ ensures \result == (this.value() < pow52(p5, p2) ? -1 : this.value() > pow52(p5, p2) ? +1 : 0);
- @*/
- public /*@ pure @*/ int cmpPow52(int p5, int p2) {
- if (p5 == 0) {
- int wordcount = p2 >> 5;
- int bitcount = p2 & 0x1f;
- int size = this.nWords + this.offset;
- if (size > wordcount + 1) {
- return 1;
- } else if (size < wordcount + 1) {
- return -1;
- }
- int a = this.data[this.nWords -1];
- int b = 1 << bitcount;
- if (a != b) {
- return ( (a & LONG_MASK) < (b & LONG_MASK)) ? -1 : 1;
- }
- return checkZeroTail(this.data, this.nWords - 1);
- }
- return this.cmp(big5pow(p5).leftShift(p2));
- }
-
- /**
- * Compares this FDBigInteger with x + y. Returns a
+ * Compares this {@link FDBigInteger} with {@code x + y}. Returns a
* value according to the comparison as:
* {@code
* -1: this < x + y
@@ -1033,10 +634,7 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
* @param y The second addend of the sum to compare.
* @return -1, 0, or 1 according to the result of the comparison.
*/
- /*@
- @ ensures \result == (this.value() < x.value() + y.value() ? -1 : this.value() > x.value() + y.value() ? +1 : 0);
- @*/
- public /*@ pure @*/ int addAndCmp(FDBigInteger x, FDBigInteger y) {
+ public int addAndCmp(FDBigInteger x, FDBigInteger y) {
FDBigInteger big;
FDBigInteger small;
int xSize = x.size();
@@ -1104,93 +702,48 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
}
/**
- * Makes this FDBigInteger immutable.
+ * Makes this {@link FDBigInteger} immutable.
+ *
+ * @return {@code this}
*/
- /*@
- @ assignable this.isImmutable;
- @ ensures this.isImmutable;
- @*/
- public void makeImmutable() {
- this.isImmutable = true;
+ public FDBigInteger makeImmutable() {
+ isImmutable = true;
+ return this;
}
/**
- * Multiplies this FDBigInteger by an integer.
+ * Multiplies this {@link FDBigInteger} by an integer.
*
- * @param i The factor by which to multiply this FDBigInteger.
- * @return This FDBigInteger multiplied by an integer.
+ * @param v The factor by which to multiply this {@link FDBigInteger}.
+ * @return This {@link FDBigInteger} multiplied by an integer.
*/
- /*@
- @ requires this.value() == 0;
- @ assignable \nothing;
- @ ensures \result == this;
- @
- @ also
- @
- @ requires this.value() != 0;
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() * UNSIGNED(i));
- @*/
- private FDBigInteger mult(int i) {
- if (this.nWords == 0) {
+ public FDBigInteger mult(int v) {
+ if (nWords == 0 || v == 0) {
return this;
}
int[] r = new int[nWords + 1];
- mult(data, nWords, i, r);
+ mult(data, nWords, v, r);
return new FDBigInteger(r, offset);
}
/**
- * Multiplies this FDBigInteger by another FDBigInteger.
+ * Multiplies this {@link FDBigInteger} by another {@link FDBigInteger}.
*
- * @param other The FDBigInteger factor by which to multiply.
- * @return The product of this and the parameter FDBigIntegers.
+ * @param other The {@link FDBigInteger} factor by which to multiply.
+ * @return The product of this and the parameter {@link FDBigInteger}s.
*/
- /*@
- @ requires this.value() == 0;
- @ assignable \nothing;
- @ ensures \result == this;
- @
- @ also
- @
- @ requires this.value() != 0 && other.value() == 0;
- @ assignable \nothing;
- @ ensures \result == other;
- @
- @ also
- @
- @ requires this.value() != 0 && other.value() != 0;
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() * other.value());
- @*/
private FDBigInteger mult(FDBigInteger other) {
- if (this.nWords == 0) {
- return this;
- }
- if (this.size() == 1) {
- return other.mult(data[0]);
- }
- if (other.nWords == 0) {
- return other;
- }
- if (other.size() == 1) {
- return this.mult(other.data[0]);
- }
int[] r = new int[nWords + other.nWords];
- mult(this.data, this.nWords, other.data, other.nWords, r);
- return new FDBigInteger(r, this.offset + other.offset);
+ mult(data, nWords, other.data, other.nWords, r);
+ return new FDBigInteger(r, offset + other.offset);
}
/**
- * Adds another FDBigInteger to this FDBigInteger.
+ * Adds another {@link FDBigInteger} to this {@link FDBigInteger}.
*
- * @param other The FDBigInteger to add.
- * @return The sum of the FDBigIntegers.
+ * @param other The {@link FDBigInteger} to add.
+ * @return The sum of the {@link FDBigInteger}s.
*/
- /*@
- @ assignable \nothing;
- @ ensures \result.value() == \old(this.value() + other.value());
- @*/
private FDBigInteger add(FDBigInteger other) {
FDBigInteger big, small;
int bigLen, smallLen;
@@ -1227,23 +780,15 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
/**
- * Multiplies a FDBigInteger by an int and adds another int. The
+ * Multiplies a {@link FDBigInteger} by an int and adds another int. The
* result is computed in place. This method is intended only to be invoked
- * from
- *
- * FDBigInteger(long lValue, char[] digits, int kDigits, int nDigits)
- * .
+ * from {@link FDBigInteger(long,char[],int,int)}.
*
- * @param iv The factor by which to multiply this FDBigInteger.
+ * @param iv The factor by which to multiply this {@link FDBigInteger}.
* @param addend The value to add to the product of this
- * FDBigInteger and iv.
+ * {@link FDBigInteger} and {@code iv}.
*/
- /*@
- @ requires this.value()*UNSIGNED(iv) + UNSIGNED(addend) < ((\bigint)1) << ((this.data.length + this.offset)*32);
- @ assignable this.data[*];
- @ ensures this.value() == \old(this.value()*UNSIGNED(iv) + UNSIGNED(addend));
- @*/
- private /*@ helper @*/ void multAddMe(int iv, int addend) {
+ private void multAdd(int iv, int addend) {
long v = iv & LONG_MASK;
// unroll 0th iteration, doing addition.
long p = v * (data[0] & LONG_MASK) + (addend & LONG_MASK);
@@ -1255,51 +800,18 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
p >>>= 32;
}
if (p != 0L) {
- data[nWords++] = (int) p; // will fail noisily if illegal!
+ data[nWords++] = (int) p;
}
}
- //
- // original doc:
- //
- // do this -=q*S
- // returns borrow
- //
/**
- * Multiplies the parameters and subtracts them from this
- * FDBigInteger.
+ * Multiplies the parameters and subtracts them from this {@link FDBigInteger}.
*
* @param q The integer parameter.
- * @param S The FDBigInteger parameter.
- * @return this - q*S.
+ * @param S The {@link FDBigInteger} parameter.
+ * @return {@code this - q*S}.
*/
- /*@
- @ ensures nWords == 0 ==> offset == 0;
- @ ensures nWords > 0 ==> data[nWords - 1] != 0;
- @*/
- /*@
- @ requires 0 < q && q <= (1L << 31);
- @ requires data != null;
- @ requires 0 <= nWords && nWords <= data.length && offset >= 0;
- @ requires !this.isImmutable;
- @ requires this.size() == S.size();
- @ requires this != S;
- @ assignable this.nWords, this.offset, this.data, this.data[*];
- @ ensures -q <= \result && \result <= 0;
- @ ensures this.size() == \old(this.size());
- @ ensures this.value() + (\result << (this.size()*32)) == \old(this.value() - q*S.value());
- @ ensures this.offset == \old(Math.min(this.offset, S.offset));
- @ ensures \old(this.offset <= S.offset) ==> this.nWords == \old(this.nWords);
- @ ensures \old(this.offset <= S.offset) ==> this.offset == \old(this.offset);
- @ ensures \old(this.offset <= S.offset) ==> this.data == \old(this.data);
- @
- @ also
- @
- @ requires q == 0;
- @ assignable \nothing;
- @ ensures \result == 0;
- @*/
- private /*@ helper @*/ long multDiffMe(long q, FDBigInteger S) {
+ private long multSub(long q, FDBigInteger S) {
long diff = 0L;
if (q != 0) {
int deltaSize = S.offset - this.offset;
@@ -1337,22 +849,15 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
return diff;
}
-
/**
* Multiplies by 10 a big integer represented as an array. The final carry
* is returned.
*
* @param src The array representation of the big integer.
- * @param srcLen The number of elements of src to use.
+ * @param srcLen The number of elements of {@code src} to use.
* @param dst The product array.
* @return The final carry of the multiplication.
*/
- /*@
- @ requires src.length >= srcLen && dst.length >= srcLen;
- @ assignable dst[0 .. srcLen - 1];
- @ ensures 0 <= \result && \result < 10;
- @ ensures AP(dst, srcLen) + (\result << (srcLen*32)) == \old(AP(src, srcLen) * 10);
- @*/
private static int multAndCarryBy10(int[] src, int srcLen, int[] dst) {
long carry = 0;
for (int i = 0; i < srcLen; i++) {
@@ -1365,157 +870,138 @@ public /*@ spec_bigint_math @*/ class FDBigInteger {
/**
* Multiplies by a constant value a big integer represented as an array.
- * The constant factor is an int.
+ * The constant factor is an {@code int}.
*
* @param src The array representation of the big integer.
- * @param srcLen The number of elements of src to use.
+ * @param srcLen The number of elements of {@code src} to use.
* @param value The constant factor by which to multiply.
* @param dst The product array.
*/
- /*@
- @ requires src.length >= srcLen && dst.length >= srcLen + 1;
- @ assignable dst[0 .. srcLen];
- @ ensures AP(dst, srcLen + 1) == \old(AP(src, srcLen) * UNSIGNED(value));
- @*/
private static void mult(int[] src, int srcLen, int value, int[] dst) {
- long val = value & LONG_MASK;
+ long v = value & LONG_MASK;
long carry = 0;
- for (int i = 0; i < srcLen; i++) {
- long product = (src[i] & LONG_MASK) * val + carry;
+ int i = 0;
+ for (; i < srcLen; i++) {
+ long product = v * (src[i] & LONG_MASK) + carry;
dst[i] = (int) product;
carry = product >>> 32;
}
- dst[srcLen] = (int) carry;
+ dst[i] = (int) carry;
}
/**
* Multiplies by a constant value a big integer represented as an array.
- * The constant factor is a long represent as two ints.
+ * The constant factor is a long represent as two {@code int}s.
*
* @param src The array representation of the big integer.
- * @param srcLen The number of elements of src to use.
+ * @param srcLen The number of elements of {@code src} to use.
* @param v0 The lower 32 bits of the long factor.
* @param v1 The upper 32 bits of the long factor.
* @param dst The product array.
*/
- /*@
- @ requires src != dst;
- @ requires src.length >= srcLen && dst.length >= srcLen + 2;
- @ assignable dst[0 .. srcLen + 1];
- @ ensures AP(dst, srcLen + 2) == \old(AP(src, srcLen) * (UNSIGNED(v0) + (UNSIGNED(v1) << 32)));
- @*/
private static void mult(int[] src, int srcLen, int v0, int v1, int[] dst) {
long v = v0 & LONG_MASK;
long carry = 0;
- for (int j = 0; j < srcLen; j++) {
+ int j = 0;
+ for (; j < srcLen; j++) {
long product = v * (src[j] & LONG_MASK) + carry;
dst[j] = (int) product;
carry = product >>> 32;
}
- dst[srcLen] = (int) carry;
+ dst[j] = (int) carry;
+
v = v1 & LONG_MASK;
carry = 0;
- for (int j = 0; j < srcLen; j++) {
- long product = (dst[j + 1] & LONG_MASK) + v * (src[j] & LONG_MASK) + carry;
- dst[j + 1] = (int) product;
+ j = 1;
+ for (; j <= srcLen; j++) {
+ long product = (dst[j] & LONG_MASK) + v * (src[j - 1] & LONG_MASK) + carry;
+ dst[j] = (int) product;
carry = product >>> 32;
}
- dst[srcLen + 1] = (int) carry;
+ dst[j] = (int) carry;
}
- // Fails assertion for negative exponent.
- /**
- * Computes 5 raised to a given power.
+ /*
+ * Lookup table of powers of 5 starting with 5^MAX_FIVE_POW.
+ * The size just serves for the conversions.
+ * It is filled lazily, except for the entries with exponent
+ * 2 (MAX_FIVE_POW - 1) and 3 (MAX_FIVE_POW - 1).
*
- * @param p The exponent of 5.
- * @return 5p.
+ * Access needs not be synchronized for thread-safety, since races would
+ * produce the same non-null value (although not the same instance).
*/
- private static FDBigInteger big5pow(int p) {
- assert p >= 0 : p; // negative power of 5
- if (p < MAX_FIVE_POW) {
- return POW_5_CACHE[p];
- }
- return big5powRec(p);
- }
+ @Stable
+ private static final FDBigInteger[] LARGE_POW_5_CACHE;
- // slow path
/**
- * Computes 5 raised to a given power.
+ * Computes 5{@code e}.
*
- * @param p The exponent of 5.
- * @return 5p.
+ * @param e The exponent of 5.
+ * @return 5{@code e}.
+ * @throws IllegalArgumentException if e > 2 - DoubleToDecimal.Q_MIN = 1076
*/
- private static FDBigInteger big5powRec(int p) {
- if (p < MAX_FIVE_POW) {
- return POW_5_CACHE[p];
+ private static FDBigInteger pow5(int e) {
+ if (e < MAX_FIVE_POW) {
+ return POW_5_CACHE[e];
}
- // construct the value.
- // recursively.
- int q, r;
- // 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
- q = p >> 1;
- r = p - q;
- FDBigInteger bigq = big5powRec(q);
- if (r < SMALL_5_POW.length) {
- return bigq.mult(SMALL_5_POW[r]);
- } else {
- return bigq.mult(big5powRec(r));
+ if (e > 2 - DoubleToDecimal.Q_MIN) {
+ throw new IllegalArgumentException("exponent too large: " + e);
}
+ FDBigInteger p5 = LARGE_POW_5_CACHE[e - MAX_FIVE_POW];
+ if (p5 == null) {
+ int ep = (e - 1) - (e - 1) % (MAX_FIVE_POW - 1);
+ p5 = (ep < MAX_FIVE_POW
+ ? POW_5_CACHE[ep]
+ : LARGE_POW_5_CACHE[ep - MAX_FIVE_POW])
+ .mult(POW_5_CACHE[e - ep]);
+ LARGE_POW_5_CACHE[e - MAX_FIVE_POW] = p5.makeImmutable();
+ }
+ return p5;
}
// for debugging ...
/**
- * Converts this FDBigInteger to a hexadecimal string.
+ * Converts this {@link FDBigInteger} to a hexadecimal string.
*
* @return The hexadecimal string representation.
*/
- public String toHexString(){
- if(nWords ==0) {
+ @Override
+ public String toString() {
+ if (nWords == 0) {
return "0";
}
- StringBuilder sb = new StringBuilder((nWords +offset)*8);
- for(int i= nWords -1; i>=0; i--) {
- String subStr = Integer.toHexString(data[i]);
- for(int j = subStr.length(); j<8; j++) {
- sb.append('0');
- }
- sb.append(subStr);
+ StringBuilder sb = new StringBuilder(8 * (size()));
+ int i = nWords - 1;
+ sb.append(Integer.toHexString(data[i--]));
+ while (i >= 0) {
+ String subStr = Integer.toHexString(data[i--]);
+ sb.repeat('0', 8 - subStr.length()).append(subStr);
}
- for(int i=offset; i>0; i--) {
- sb.append("00000000");
- }
- return sb.toString();
+ return sb.repeat('0', 8 * offset).toString();
}
// for debugging ...
/**
- * Converts this FDBigInteger to a BigInteger.
+ * Converts this {@link FDBigInteger} to a {@code byte[]} suitable
+ * for use with {@link java.math.BigInteger#BigInteger(byte[])}.
*
- * @return The BigInteger representation.
+ * @return The {@code byte[]} representation.
*/
- public BigInteger toBigInteger() {
- byte[] magnitude = new byte[nWords * 4 + 1];
- for (int i = 0; i < nWords; i++) {
+ /*
+ * A toBigInteger() method would be more convenient, but it would introduce
+ * a dependency on java.math, which is not desirable in such a basic layer
+ * like this one used in components as java.lang, javac, and others.
+ */
+ public byte[] toByteArray() {
+ byte[] magnitude = new byte[4 * size() + 1]; // +1 for the "sign" byte
+ for (int i = 0, j = magnitude.length - 4 * offset; i < nWords; i += 1, j -= 4) {
int w = data[i];
- magnitude[magnitude.length - 4 * i - 1] = (byte) w;
- magnitude[magnitude.length - 4 * i - 2] = (byte) (w >> 8);
- magnitude[magnitude.length - 4 * i - 3] = (byte) (w >> 16);
- magnitude[magnitude.length - 4 * i - 4] = (byte) (w >> 24);
+ magnitude[j - 1] = (byte) w;
+ magnitude[j - 2] = (byte) (w >> 8);
+ magnitude[j - 3] = (byte) (w >> 16);
+ magnitude[j - 4] = (byte) (w >> 24);
}
- return new BigInteger(magnitude).shiftLeft(offset * 32);
+ return magnitude;
}
- // for debugging ...
- /**
- * Converts this FDBigInteger to a string.
- *
- * @return The string representation.
- */
- @Override
- public String toString(){
- return toBigInteger().toString();
- }
}
diff --git a/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java b/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java
index e90ca4d75f1..067ca9a6406 100644
--- a/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java
+++ b/src/java.base/share/classes/jdk/internal/math/FloatingDecimal.java
@@ -27,59 +27,50 @@ package jdk.internal.math;
import jdk.internal.vm.annotation.Stable;
+import static jdk.internal.math.FDBigInteger.valueOfMulPow52;
+import static jdk.internal.math.FDBigInteger.valueOfPow52;
+
/**
* A class for converting between ASCII and decimal representations of a single
* or double precision floating point number. Most conversions are provided via
- * static convenience methods, although a BinaryToASCIIConverter
- * instance may be obtained and reused.
+ * static convenience methods, although a {@link BinaryToASCIIBuffer}
+ * instance may be obtained.
*/
-public class FloatingDecimal{
+public final class FloatingDecimal {
//
// Constants of the implementation;
// most are IEEE-754 related.
- // (There are more really boring constants at the end.)
//
- static final int EXP_SHIFT = DoubleConsts.SIGNIFICAND_WIDTH - 1;
- static final long FRACT_HOB = ( 1L<String to a double precision floating point value.
+ * Converts a {@link String} to a double precision floating point value.
*
- * @param s The String to convert.
+ * @param s The {@link String} to convert.
* @return The double precision value.
- * @throws NumberFormatException If the String does not
+ * @throws NumberFormatException If the {@link String} does not
* represent a properly formatted double precision value.
*/
public static double parseDouble(String s) throws NumberFormatException {
- return readJavaFormatString(s, BINARY_64_IX).doubleValue();
+ return readJavaFormatString(s, BINARY_64_IX);
}
/**
- * Converts a String to a single precision floating point value.
+ * Converts a {@link String} to a single precision floating point value.
*
- * @param s The String to convert.
+ * @param s The {@link String} to convert.
* @return The single precision value.
- * @throws NumberFormatException If the String does not
+ * @throws NumberFormatException If the {@link String} does not
* represent a properly formatted single precision value.
*/
public static float parseFloat(String s) throws NumberFormatException {
- return readJavaFormatString(s, BINARY_32_IX).floatValue();
+ return (float) readJavaFormatString(s, BINARY_32_IX);
}
/**
@@ -87,60 +78,23 @@ public class FloatingDecimal{
* double value
*
* @param decExp The decimal exponent of the value to generate
- * @param digits The digits of the significand.
+ * @param d The digits of the significand.
* @param length Number of digits to use
* @return The double-precision value of the conversion
*/
- public static double parseDoubleSignlessDigits(int decExp, byte[] digits, int length) {
- return readDoubleSignlessDigits(decExp, digits, length).doubleValue();
- }
-
- /**
- * A converter which can process single or double precision floating point
- * values into an ASCII String representation.
- */
- public interface BinaryToASCIIConverter {
-
- /**
- * Retrieves the decimal exponent most closely corresponding to this value.
- * @return The decimal exponent.
- */
- int getDecimalExponent();
-
- /**
- * Retrieves the value as an array of digits.
- * @param digits The digit array.
- * @return The number of valid digits copied into the array.
- */
- int getDigits(byte[] digits);
-
- /**
- * Indicates whether the value was rounded up during the binary to ASCII
- * conversion.
- *
- * @return true if and only if the value was rounded up.
- */
- boolean digitsRoundedUp();
-
- /**
- * Indicates whether the binary to ASCII conversion was exact.
- *
- * @return true if any only if the conversion was exact.
- */
- boolean decimalDigitsExact();
+ public static double parseDoubleSignlessDigits(int decExp, byte[] d, int length) {
+ return new ASCIIToBinaryBuffer(false, decExp, d, length).doubleValue();
}
private static final String INFINITY_REP = "Infinity";
private static final String NAN_REP = "NaN";
- private static final BinaryToASCIIConverter B2AC_POSITIVE_ZERO = new BinaryToASCIIBuffer(false, new byte[]{'0'});
- private static final BinaryToASCIIConverter B2AC_NEGATIVE_ZERO = new BinaryToASCIIBuffer(true, new byte[]{'0'});
+ private static final BinaryToASCIIBuffer B2AC_POSITIVE_ZERO =
+ new BinaryToASCIIBuffer(new byte[] {'0'});
+ private static final BinaryToASCIIBuffer B2AC_NEGATIVE_ZERO =
+ new BinaryToASCIIBuffer(new byte[] {'0'});
- /**
- * A buffered implementation of BinaryToASCIIConverter.
- */
- static class BinaryToASCIIBuffer implements BinaryToASCIIConverter {
- private boolean isNegative;
+ public static final class BinaryToASCIIBuffer {
private int decExponent;
private int firstDigitIndex;
private int nDigits;
@@ -161,48 +115,39 @@ public class FloatingDecimal{
/**
* Default constructor; used for non-zero values,
- * BinaryToASCIIBuffer may be thread-local and reused
+ * {@link BinaryToASCIIBuffer} may be thread-local and reused
*/
- BinaryToASCIIBuffer(){
+ private BinaryToASCIIBuffer() {
this.digits = new byte[20];
}
/**
* Creates a specialized value (positive and negative zeros).
*/
- BinaryToASCIIBuffer(boolean isNegative, byte[] digits){
- this.isNegative = isNegative;
+ private BinaryToASCIIBuffer(byte[] digits){
this.decExponent = 0;
this.digits = digits;
this.firstDigitIndex = 0;
this.nDigits = digits.length;
}
- @Override
public int getDecimalExponent() {
return decExponent;
}
- @Override
public int getDigits(byte[] digits) {
System.arraycopy(this.digits, firstDigitIndex, digits, 0, this.nDigits);
return this.nDigits;
}
- @Override
public boolean digitsRoundedUp() {
return decimalDigitsRoundedUp;
}
- @Override
public boolean decimalDigitsExact() {
return exactDecimalConversion;
}
- private void setSign(boolean isNegative) {
- this.isNegative = isNegative;
- }
-
/**
* This is the easy subcase --
* all the significant bits, after scaling, are held in lvalue.
@@ -212,13 +157,14 @@ public class FloatingDecimal{
* In particular:
* lvalue is a finite number (not Inf, nor NaN)
* lvalue > 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!
*/
- private void developLongDigits( int decExponent, long lvalue, int insignificantDigits ){
+ private void developLongDigits( long lvalue, int insignificantDigits ){
+ int decExponent = 0;
if ( insignificantDigits != 0 ){
// Discard non-significant low-order bits, while rounding,
// up to insignificant value.
@@ -275,7 +221,7 @@ public class FloatingDecimal{
this.nDigits = this.digits.length - digitno;
}
- private void dtoa( int binExp, long fractBits, int nSignificantBits, boolean isCompatibleFormat)
+ private void dtoa( int binExp, long fractBits, int nSignificantBits)
{
assert fractBits > 0 ; // fractBits here can't be zero or negative
assert (fractBits & FRACT_HOB)!=0 ; // Hi-order bit should be set
@@ -327,7 +273,7 @@ public class FloatingDecimal{
} else {
fractBits >>>= (EXP_SHIFT-binExp) ;
}
- developLongDigits( 0, fractBits, insignificant );
+ developLongDigits( fractBits, insignificant );
return;
}
//
@@ -421,7 +367,7 @@ public class FloatingDecimal{
// 26 Sept 96 is not that day.
// So we use a symmetric test.
//
- int ndigit = 0;
+ int ndigit;
boolean low, high;
long lowDigitDifference;
int q;
@@ -477,7 +423,7 @@ public class FloatingDecimal{
// Thus we will need more than one digit if we're using
// E-form
//
- if ( !isCompatibleFormat ||decExp < -3 || decExp >= 8 ){
+ if (decExp < -3 || decExp >= 8){
high = low = false;
}
while( ! low && ! high ){
@@ -531,7 +477,7 @@ public class FloatingDecimal{
// Thus we will need more than one digit if we're using
// E-form
//
- if ( !isCompatibleFormat || decExp < -3 || decExp >= 8 ){
+ if (decExp < -3 || decExp >= 8){
high = low = false;
}
while( ! low && ! high ){
@@ -561,14 +507,14 @@ public class FloatingDecimal{
// We really must do FDBigInteger arithmetic.
// Fist, construct our FDBigInteger initial values.
//
- FDBigInteger Sval = FDBigInteger.valueOfPow52(S5, S2);
+ FDBigInteger Sval = valueOfPow52(S5, S2);
int shiftBias = Sval.getNormalizationBias();
Sval = Sval.leftShift(shiftBias); // normalize so that division works better
- FDBigInteger Bval = FDBigInteger.valueOfMulPow52(fractBits, B5, B2 + shiftBias);
- FDBigInteger Mval = FDBigInteger.valueOfPow52(M5 + 1, M2 + shiftBias + 1);
+ FDBigInteger Bval = valueOfMulPow52(fractBits, B5, B2 + shiftBias);
+ FDBigInteger Mval = valueOfPow52(M5 + 1, M2 + shiftBias + 1);
- FDBigInteger tenSval = FDBigInteger.valueOfPow52(S5 + 1, S2 + shiftBias + 1); //Sval.mult( 10 );
+ FDBigInteger tenSval = valueOfPow52(S5 + 1, S2 + shiftBias + 1); //Sval.mult( 10 );
//
// Unroll the first iteration. If our decExp estimate
// was too high, our first quotient will be zero. In this
@@ -592,7 +538,7 @@ public class FloatingDecimal{
// Thus we will need more than one digit if we're using
// E-form
//
- if (!isCompatibleFormat || decExp < -3 || decExp >= 8 ){
+ if (decExp < -3 || decExp >= 8){
high = low = false;
}
while( ! low && ! high ){
@@ -609,7 +555,7 @@ public class FloatingDecimal{
} else {
lowDigitDifference = 0L; // this here only for flow analysis!
}
- exactDecimalConversion = (Bval.cmp( FDBigInteger.ZERO ) == 0);
+ exactDecimalConversion = Bval.isZero();
}
this.decExponent = decExp+1;
this.firstDigitIndex = 0;
@@ -662,7 +608,7 @@ public class FloatingDecimal{
/**
* 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)
@@ -670,7 +616,7 @@ public class FloatingDecimal{
* log10(d) ~=~ log10(d2) + binExp * log10(2)
* take the floor and call it decExp.
*/
- static int estimateDecExp(long fractBits, int binExp) {
+ private static int estimateDecExp(long fractBits, int binExp) {
double d2 = Double.longBitsToDouble( EXP_ONE | ( fractBits & DoubleConsts.SIGNIF_BIT_MASK ) );
double d = (d2-1.5D)*0.289529654D + 0.176091259 + (double)binExp * 0.301029995663981;
long dBits = Double.doubleToRawLongBits(d); //can't be NaN here so use raw
@@ -705,16 +651,18 @@ public class FloatingDecimal{
* for ( i = 0; insignificant >= 10L; i++ )
* insignificant /= 10L;
*/
+ @Stable
private static final int[] insignificantDigitsNumber = {
0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3,
4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7,
8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11,
12, 12, 12, 12, 13, 13, 13, 14, 14, 14,
15, 15, 15, 15, 16, 16, 16, 17, 17, 17,
- 18, 18, 18, 19
+ 18, 18, 18, 19,
};
// approximately ceil( log2( long5pow[i] ) )
+ @Stable
private static final int[] N_5_BITS = {
0,
3,
@@ -743,760 +691,508 @@ public class FloatingDecimal{
56,
59,
61,
+ 63,
};
}
private static final ThreadLocal threadLocalBinaryToASCIIBuffer =
- new ThreadLocal() {
- @Override
- protected BinaryToASCIIBuffer initialValue() {
- return new BinaryToASCIIBuffer();
- }
- };
+ ThreadLocal.withInitial(BinaryToASCIIBuffer::new);
private static BinaryToASCIIBuffer getBinaryToASCIIBuffer() {
return threadLocalBinaryToASCIIBuffer.get();
}
- /**
- * A converter which can process an ASCII String representation
- * of a single or double precision floating point value into a
- * float or a double.
+ /*
+ * The mathematical value x of an instance is
+ * ±<0.d_1...d_n> 10^e
+ * where d_i = d[i-1] - '0' (0 < i ≤ n) is the i-th digit.
+ * It is assumed that d_1 > 0.
+ * isNegative denotes the - sign.
*/
- interface ASCIIToBinaryConverter {
+ private static final class ASCIIToBinaryBuffer {
- double doubleValue();
+ private final boolean isNegative;
+ private final int e;
+ private final int n;
+ private final byte[] d;
- float floatValue();
-
- }
-
- /**
- * A ASCIIToBinaryConverter container for a double.
- */
- static class PreparedASCIIToBinaryBuffer implements ASCIIToBinaryConverter {
- private final double doubleVal;
- private final float floatVal;
-
- public PreparedASCIIToBinaryBuffer(double doubleVal, float floatVal) {
- this.doubleVal = doubleVal;
- this.floatVal = floatVal;
+ private ASCIIToBinaryBuffer(boolean isNegative, int e, byte[] d, int n) {
+ this.isNegative = isNegative;
+ this.e = e;
+ this.d = d;
+ this.n = n;
}
- @Override
- public double doubleValue() {
- return doubleVal;
+ /* Assumes n ≤ 19 and returns a decimal prefix of f as an unsigned long. */
+ private long toLong(int n) {
+ long f = 0;
+ for (int i = 0; i < n; ++i) {
+ f = 10 * f + (d[i] - '0');
+ }
+ return f;
}
- @Override
- public float floatValue() {
- return floatVal;
- }
- }
+ private double doubleValue() {
+ /*
+ * As described above, the magnitude of the mathematical value is
+ * x = <0.d_1...d_n> 10^e = 10^(e-n) = f 10^ep
+ * where f = and ep = e - n are integers.
+ *
+ * Let r_e denote the roundTiesToEven rounding.
+ * This method returns ±r_e(x).
+ */
- static final ASCIIToBinaryConverter A2BC_POSITIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
- static final ASCIIToBinaryConverter A2BC_NEGATIVE_INFINITY = new PreparedASCIIToBinaryBuffer(Double.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
- static final ASCIIToBinaryConverter A2BC_NOT_A_NUMBER = new PreparedASCIIToBinaryBuffer(Double.NaN, Float.NaN);
- static final ASCIIToBinaryConverter A2BC_POSITIVE_ZERO = new PreparedASCIIToBinaryBuffer(0.0d, 0.0f);
- static final ASCIIToBinaryConverter A2BC_NEGATIVE_ZERO = new PreparedASCIIToBinaryBuffer(-0.0d, -0.0f);
+ /* Filter out extremely small or extremely large x. */
+ if (e <= DoubleToDecimal.E_THR_Z) {
+ /* Test cases: "0.9e-324", "3e-500" */
+ return signed(0.0);
+ }
+ if (e >= DoubleToDecimal.E_THR_I) {
+ /* Test cases: "0.1e310", "4e500" */
+ return signed(Double.POSITIVE_INFINITY);
+ }
- /**
- * A buffered implementation of ASCIIToBinaryConverter.
- */
- static class ASCIIToBinaryBuffer implements ASCIIToBinaryConverter {
- final boolean isNegative;
- final int decExponent;
- final byte[] digits;
- int nDigits;
+ /*
+ * Attempt some fast paths before resorting to higher precision.
+ * Here, let P = Double.PRECISION = 53.
+ *
+ * Below, fl is an unsigned long, thus we require n ≤ 19 because
+ * 10^19 < 2^64 < 10^20.
+ */
+ int n = this.n;
+ int ep = e - n;
+ double v;
+ int m = Math.min(n, MathUtils.N);
+ long fl = toLong(m); // unsigned
+ if (n <= MathUtils.N && 0 <= ep && e <= MathUtils.N) {
+ /*
+ * Here, n ≤ 19, hence f = fl < 10^19.
+ * Since e = n + ep and 0 ≤ ep ∧ n + ep ≤ 19 we see that
+ * x = f 10^ep < 10^n 10^ep = 10^(n+ep) ≤ 10^19.
+ * Thus, x = fl 10^ep fits in an unsigned long as well.
+ * If its most significant bit is 0, the long is non-negative.
+ * Otherwise, fl ≥ 2^63, so there's room for P precision bits,
+ * +1 rounding bit, +1 sticky bit.
+ * In both cases, correct rounding is achieved as below.
+ * All integer x < 10^19 are covered here.
+ */
- ASCIIToBinaryBuffer( boolean negSign, int decExponent, byte[] digits, int n)
- {
- this.isNegative = negSign;
- this.decExponent = decExponent;
- this.digits = digits;
- this.nDigits = n;
+ /*
+ * Test cases:
+ * for fl < 2^63: "1", "2.34000e2", "9.223e18";
+ * for fl ≥ 2^63: "9.876e18", "9223372036854776833" (this
+ * is 2^63 + 2^10 + 1, rounding up due to sticky bit),
+ * "9223372036854776832" (this is 2^63 + 2^10, halfway
+ * value rounding down to even);
+ */
+ fl *= MathUtils.pow10(ep); // 0 ≤ ep < 19
+ v = fl >= 0 ? fl : 2.0 * (fl >>> 1 | fl & 0b1);
+ return signed(v);
+ }
+
+ if (n <= FLOG_10_MAX_LONG && -MAX_SMALL_TEN <= ep) {
+ v = fl;
+ /*
+ * Here, -22 ≤ ep.
+ * Further, fl < 10^18, so fl is an exact double iff
+ * (long) v == fl holds.
+ * If fl is not an exact double, resort to higher precision.
+ */
+ boolean isExact = (long) v == fl;
+ if (isExact && ep <= MAX_SMALL_TEN) {
+ /*
+ * Here, -22 ≤ ep ≤ 22, so 10^|ep| is an exact double.
+ * The product or quotient below operate on exact doubles,
+ * so the result is correctly rounded.
+ */
+
+ /*
+ * Test cases:
+ * for ep < 0: "1.23", "0.000234";
+ * for ep > 0: "3.45e23", "576460752303423616e20" (the
+ * significand is 2^59 + 2^7, an exact double);
+ */
+ v = ep >= 0 ? v * SMALL_10_POW[ep] : v / SMALL_10_POW[-ep];
+ return signed(v);
+ }
+
+ /*
+ * Here, fl < 10^18 is not an exact double, or ep > 22.
+ * If fl is not an exact double, resort to higher precision.
+ */
+ if (isExact) { // v and fl are mathematically equal.
+ /*
+ * Here, ep > 22.
+ * We have f = fl = v.
+ * Note that 2^P = 9007199254740992 has 16 digits.
+ * If f does not start with 9 let ef = 16 - n, otherwise
+ * let ef = 15 - n.
+ * If ef < 0 then resort to higher precision.
+ * Otherwise, if f does not start with 9 we have n ≤ 16,
+ * so f 10^ef < 9 10^(n-1) 10^ef = 9 10^15 < 2^P.
+ * If f starts with 9 we have n ≤ 15, hence f 10^ef <
+ * 10^n 10^ef = 10^15 < 2^P.
+ *
+ * Hence, when ef ≥ 0 and ep - ef ≤ 22 we know that
+ * fl 10^ep = (fl 10^ef) 10^(ep-ef), with fl, (fl 10^ef),
+ * and 10^(ep-ef) all exact doubles.
+ */
+ int ef = (d[0] < '9' ? MAX_DEC_DIGITS + 1 : MAX_DEC_DIGITS) - n;
+ if (ef >= 0 && ep - ef <= MAX_SMALL_TEN) {
+ /*
+ * Test cases:
+ * f does not start with 9: "1e37", "8999e34";
+ * f starts with 9: "0.9999e36", "0.9876e37";
+ */
+
+ /* Rely on left-to-right evaluation. */
+ v = v * SMALL_10_POW[ef] * SMALL_10_POW[ep - ef];
+ return signed(v);
+ }
+ }
+ }
+
+ /*
+ * Here, the above fast paths have failed to return.
+ * Force ll, lh in [10^(N-1), 10^N] to have more high order bits.
+ */
+ long ll = fl; // unsigned
+ long lh; // unsigned
+ if (n <= MathUtils.N) { // ll = f
+ ll *= MathUtils.pow10(MathUtils.N - n);
+ lh = ll;
+ } else { // ll is an N digits long prefix of f
+ lh = ll + 1;
+ }
+ int el = e - MathUtils.N;
+ /*
+ * We now have
+ * x = f 10^ep
+ * ll 10^el ≤ x ≤ lh 10^el
+ * 2^59 < 10^(N-1) ≤ ll ≤ lh ≤ 10^N < 2^64
+ *
+ * Rather than rounding x directly, which requires full precision
+ * arithmetic, approximate x as follows.
+ * Let integers g and r such that (see comments in MathUtils)
+ * (g - 1) 2^r ≤ 10^el < g 2^r
+ * and split g into the lower 63 bits g0 and the higher bits g1:
+ * g = g1 2^63 + g0
+ * where
+ * 2^62 < g1 + 1 < 2^63, 0 < g0 < 2^63
+ * We have
+ * g - 1 = g1 2^63 + g0 - 1 ≥ g1 2^63
+ * g = g1 2^63 + g0 < g1 2^63 + 2^63 = (g1 + 1) 2^63
+ * Let
+ * nl = ll g1 nh = lh (g1 + 1)
+ * These lead to
+ * nl 2^(r+63) ≤ x < nh 2^(r+63)
+ * Let
+ * v = r_e(nl 2^(r+63)) vh = r_e(nh 2^(r+63))
+ * If v = vh then r_e(x) = v.
+ *
+ * We also have
+ * 2^121 = 2^59 2^62 < nl < nh < 2^64 2^63 = 2^127
+ * Therefore, each of nl and nh fits in two longs.
+ * Split them into the lower 64 bits and the higher bits.
+ * nl = nl1 2^64 + nl0 2^57 ≤ nl1 < 2^63
+ * nh = nh1 2^64 + nh0 2^57 ≤ nh1 < 2^63
+ * Let bl and bh be the bitlength of nl1 and nh1, resp.
+ * Both bl and bh lie in the interval [58, 63], and all of nl1, nh1,
+ * nl, and nh are in the normal range of double.
+ * As nl ≤ nh ≤ nl + 2 ll, and as ll < 2^64, then either bh = bl,
+ * or more rarely bh = bl + 1.
+ *
+ * As mentioned above, if v = vh then r_e(x) = v.
+ * Rather than rounding nl 2^(r+63), nh 2^(r+63) boundaries directly,
+ * first round nl and nh to obtain doubles wl and wh, resp.
+ * wl = r_e(nl) wh = r_e(nh)
+ * Note that both wl and wh are normal doubles.
+ *
+ * Assume wl = wh.
+ * There's a good chance that v = scalb(wl, r + 63) holds.
+ * In fact, if x ≥ MIN_NORMAL then it can be (tediously) shown that
+ * v = scalb(wl, r + 63) holds, even when v overflows.
+ * If x < MIN_NORMAL, and since wl is normal and v ≤ MIN_NORMAL,
+ * the precision might be lowered, so scalb(wl, r + 63) might incur
+ * two rounding errors and could slightly differ from v.
+ *
+ * It is costly to precisely determine whether x ≥ MIN_NORMAL.
+ * However, bl + r > MIN_EXPONENT - 127 implies x ≥ MIN_NORMAL,
+ * and bh + r ≤ MIN_EXPONENT - 127 entails x < MIN_NORMAL.
+ * Finally, when bl + r ≤ MIN_EXPONENT - 127 < bh + r we see that
+ * bl + r = MIN_EXPONENT - 127 and bh = bl + 1 must hold.
+ *
+ * As noted, nh ≤ nl + 2 ll.
+ * This means
+ * nh1 ≤ nh 2^(-64) ≤ (nl + 2 ll) 2^(-64) < (nl1 + 1) + 2
+ * and thus
+ * nh1 ≤ nl1 + 2
+ */
+ int rp = MathUtils.flog2pow10(el) + 2; // r + 127
+ long g1 = MathUtils.g1(el);
+ long nl1 = Math.unsignedMultiplyHigh(ll, g1);
+ long nl0 = ll * g1;
+ long nh1 = Math.unsignedMultiplyHigh(lh, g1 + 1);
+ long nh0 = lh * (g1 + 1);
+ int bl = Long.SIZE - Long.numberOfLeadingZeros(nl1);
+ if (bl + rp > Double.MIN_EXPONENT) { // implies x is normal
+ /*
+ * To round nl we need its most significant P bits, the rounding
+ * bit immediately to the right, and an indication (sticky bit)
+ * of whether there are "1" bits following the rounding bit.
+ * The sticky bit can be placed anywhere after the rounding bit.
+ * Since bl ≥ 58, the P = 53 bits, the rounding bit, and space
+ * for the sticky bit are all located in nl1.
+ *
+ * When nl0 = 0, the indication of whether there are "1" bits
+ * to the right of the rounding bit is already contained in nl1.
+ * Rounding nl to wl is the same as rounding nl1 to ul and then
+ * multiplying this by 2^64.
+ * that is, given wl = r_e(nl), ul = r_e(nl1), we get
+ * wl = scalb(ul, 64).
+ * The same holds for nh, wh, nh1, and uh.
+ * So, if ul = uh then wl = wh, thus v = scalb(ul, r + 127).
+ *
+ * When nl1 ≠ 0, there are indeed "1" bits to the right of the
+ * rounding bit.
+ * We force the rightmost bit of nl1 to 1, obtaining nl1'.
+ * Then, again, rounding nl to wl is the same as rounding nl1'
+ * to ul and multiplying this by 2^64.
+ * Analogously for nh, wh, nh1, and uh.
+ * Again, if ul = uh then wl = wh, thus v = scalb(ul, r + 127).
+ *
+ * Since nh1 ≤ nl1 + 2, then either uh = ul or uh = nextUp(ul).
+ * This means that when ul ≠ uh then
+ * v ≤ r_e(x) ≤ nextUp(v)
+ */
+ double ul = nl1 | (nl0 != 0 ? 1 : 0);
+ double uh = nh1 | (nh0 != 0 ? 1 : 0);
+ v = Math.scalb(ul, rp);
+ if (ul == uh || v == Double.POSITIVE_INFINITY) {
+ /*
+ * Test cases:
+ * for ll = lh ∧ ul = uh: "1.2e-200", "2.3e100";
+ * for ll ≠ lh ∧ ul = uh: "1.2000000000000000003e-200",
+ * "2.3000000000000000004e100";
+ * for ll = lh ∧ v = ∞: "5.249320425370670463e308";
+ * for ll ≠ lh ∧ v = ∞: "5.2493204253706704633e308";
+ */
+ return signed(v);
+ }
+ } else {
+ int bh = Long.SIZE - Long.numberOfLeadingZeros(nh1);
+ if (bh + rp <= Double.MIN_EXPONENT) { // implies x is subnormal
+ /*
+ * We need to reduce the precision to avoid double rounding
+ * issues.
+ * Shifting to the right while keeping room for the rounding
+ * and the sticky bit is one way to go.
+ * Other than that, the reasoning is similar to the above case.
+ */
+ int sh = DoubleToDecimal.Q_MIN - rp; // shift distance
+ long sbMask = -1L >>> 1 - sh;
+
+ long nl1p = nl1 >>> sh;
+ long rb = nl1 >>> sh - 1;
+ long sb = (nl1 & sbMask | nl0) != 0 ? 1 : 0;
+ long corr = rb & (sb | nl1p) & 0b1;
+ double ul = nl1p + corr;
+
+ long nh1p = nh1 >>> sh;
+ rb = nh1 >>> sh - 1;
+ sb = (nh1 & sbMask | nh0) != 0 ? 1 : 0;
+ corr = rb & (sb | nh1p) & 0b1;
+ double uh = nh1p + corr;
+ v = Math.scalb(ul, rp + sh);
+ if (ul == uh) {
+ /*
+ * Test cases:
+ * for ll = lh: "1.2e-320";
+ * for ll ≠ lh: "1.2000000000000000003e-320";
+ */
+ return signed(v);
+ }
+ } else {
+ /*
+ * Here, bl + r ≤ MIN_EXPONENT - 127 < bh + r.
+ * As mentioned before, this means bh = bl + 1 and
+ * rp = MIN_EXPONENT - bl.
+ * As nh1 ≤ nl1 + 2, nl1 ≥ 2^57, bh = bl + 1 happens only if
+ * the most significant P + 2 bits in nl1 are all "1" bits,
+ * so wl = r_e(nl) = r_e(nh) = wh = 2^(bl+64), and
+ * thus v = vh = 2^(bl+127) = 2^MIN_EXPONENT = MIN_NORMAL.
+ */
+
+ /*
+ * Test cases:
+ * for ll = lh: "2.225073858507201383e-308"
+ * for ll ≠ lh: "2.2250738585072013831e-308"
+ */
+ return signed(Double.MIN_NORMAL);
+ }
+ }
+
+ /*
+ * Measurements show that the failure rate of the above fast paths
+ * on the outcomes of Double.toString() on uniformly distributed
+ * double bit patterns is around 0.04%.
+ *
+ * Here, v ≤ r_e(x) ≤ nextUp(v), with v = c 2^q (c, q are as in
+ * IEEE-754 2019).
+ *
+ * Let vr = v + ulp(v)/2 = (c + 1/2) 2^q, the number halfway between
+ * v and nextUp(v).
+ * With cr = (2 c + 1), qr = q - 1 we get vr = cr 2^qr.
+ */
+ long bits = Double.doubleToRawLongBits(v);
+ int be = (int) ((bits & DoubleConsts.EXP_BIT_MASK) >>> DoubleConsts.SIGNIFICAND_WIDTH - 1);
+ int qr = be - (DoubleConsts.EXP_BIAS + DoubleConsts.SIGNIFICAND_WIDTH - 1)
+ - (be != 0 ? 1 : 0);
+ long cr = 2 * (bits & DoubleConsts.SIGNIF_BIT_MASK | (be != 0 ? DoubleToDecimal.C_MIN : 0)) + 1;
+
+ /*
+ * The test vr ⋚ x is equivalent to cr 2^qr ⋚ f 10^ep.
+ * This is in turn equivalent to one of 4 cases, where all exponents
+ * are non-negative:
+ * ep ≥ 0 ∧ ep ≥ qr: cr ⋚ f 5^ep 2^(ep-qr)
+ * ep ≥ 0 ∧ ep < qr: cr 2^(qr-ep) ⋚ f 5^ep
+ * ep < 0 ∧ ep ≥ qr: cr 5^(-ep) ⋚ f 2^(ep-qr)
+ * ep < 0 ∧ ep < qr: cr 5^(-ep) 2^(qr-ep) ⋚ f
+ */
+ FDBigInteger lhs = valueOfMulPow52(cr, Math.max(-ep, 0), Math.max(qr - ep, 0));
+ FDBigInteger rhs = new FDBigInteger(fl, d, m, n)
+ .multByPow52(Math.max(ep, 0), Math.max(ep - qr, 0));
+ int cmp = lhs.cmp(rhs);
+ v = Double.longBitsToDouble(cmp < 0
+ ? bits + 1
+ : cmp > 0
+ ? bits
+ : bits + (bits & 0b1));
+ return signed(v);
}
- /**
- * Takes a FloatingDecimal, which we presumably just scanned in,
- * and finds out what its value is, as a double.
- *
- * AS A SIDE EFFECT, SET roundDir TO INDICATE PREFERRED
- * ROUNDING DIRECTION in case the result is really destined
- * for a single-precision float.
- */
- @Override
- public double doubleValue() {
- int kDigits = Math.min(nDigits, MAX_DECIMAL_DIGITS + 1);
- //
- // convert the lead kDigits to a long integer.
- //
- // (special performance hack: start to do it using int)
- int iValue = (int) digits[0] - (int) '0';
- int iDigits = Math.min(kDigits, INT_DECIMAL_DIGITS);
- for (int i = 1; i < iDigits; i++) {
- iValue = iValue * 10 + (int) digits[i] - (int) '0';
- }
- long lValue = (long) iValue;
- for (int i = iDigits; i < kDigits; i++) {
- lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0');
- }
- double dValue = (double) lValue;
- int exp = decExponent - kDigits;
- //
- // lValue now contains a long integer with the value of
- // the first kDigits digits of the number.
- // dValue contains the (double) of the same.
- //
-
- if (nDigits <= MAX_DECIMAL_DIGITS) {
- //
- // possibly an easy case.
- // We know that the digits can be represented
- // exactly. And if the exponent isn't too outrageous,
- // the whole thing can be done with one operation,
- // thus one rounding error.
- // Note that all our constructors trim all leading and
- // trailing zeros, so simple values (including zero)
- // will always end up here
- //
- if (exp == 0 || dValue == 0.0) {
- return (isNegative) ? -dValue : dValue; // small floating integer
- }
- else if (exp >= 0) {
- if (exp <= MAX_SMALL_TEN) {
- //
- // Can get the answer with one operation,
- // thus one roundoff.
- //
- double rValue = dValue * SMALL_10_POW[exp];
- return (isNegative) ? -rValue : rValue;
- }
- int slop = MAX_DECIMAL_DIGITS - kDigits;
- if (exp <= MAX_SMALL_TEN + slop) {
- //
- // We can multiply dValue by 10^(slop)
- // and it is still "small" and exact.
- // Then we can multiply by 10^(exp-slop)
- // with one rounding.
- //
- dValue *= SMALL_10_POW[slop];
- double rValue = dValue * SMALL_10_POW[exp - slop];
- return (isNegative) ? -rValue : rValue;
- }
- //
- // Else we have a hard case with a positive exp.
- //
- } else {
- if (exp >= -MAX_SMALL_TEN) {
- //
- // Can get the answer in one division.
- //
- double rValue = dValue / SMALL_10_POW[-exp];
- return (isNegative) ? -rValue : rValue;
- }
- //
- // Else we have a hard case with a negative exp.
- //
- }
- }
-
- //
- // Harder cases:
- // The sum of digits plus exponent is greater than
- // what we think we can do with one error.
- //
- // Start by approximating the right answer by,
- // naively, scaling by powers of 10.
- //
- if (exp > 0) {
- if (decExponent > MAX_DECIMAL_EXPONENT + 1) {
- //
- // Lets face it. This is going to be
- // Infinity. Cut to the chase.
- //
- return (isNegative) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
- }
- if ((exp & 15) != 0) {
- dValue *= SMALL_10_POW[exp & 15];
- }
- if ((exp >>= 4) != 0) {
- int j;
- for (j = 0; exp > 1; j++, exp >>= 1) {
- if ((exp & 1) != 0) {
- dValue *= BIG_10_POW[j];
- }
- }
- //
- // The reason for the weird exp > 1 condition
- // in the above loop was so that the last multiply
- // would get unrolled. We handle it here.
- // It could overflow.
- //
- double t = dValue * BIG_10_POW[j];
- if (Double.isInfinite(t)) {
- //
- // It did overflow.
- // Look more closely at the result.
- // If the exponent is just one too large,
- // then use the maximum finite as our estimate
- // value. Else call the result infinity
- // and punt it.
- // ( I presume this could happen because
- // rounding forces the result here to be
- // an ULP or two larger than
- // Double.MAX_VALUE ).
- //
- t = dValue / 2.0;
- t *= BIG_10_POW[j];
- if (Double.isInfinite(t)) {
- return (isNegative) ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY;
- }
- t = Double.MAX_VALUE;
- }
- dValue = t;
- }
- } else if (exp < 0) {
- exp = -exp;
- if (decExponent < MIN_DECIMAL_EXPONENT - 1) {
- //
- // Lets face it. This is going to be
- // zero. Cut to the chase.
- //
- return (isNegative) ? -0.0 : 0.0;
- }
- if ((exp & 15) != 0) {
- dValue /= SMALL_10_POW[exp & 15];
- }
- if ((exp >>= 4) != 0) {
- int j;
- for (j = 0; exp > 1; j++, exp >>= 1) {
- if ((exp & 1) != 0) {
- dValue *= TINY_10_POW[j];
- }
- }
- //
- // The reason for the weird exp > 1 condition
- // in the above loop was so that the last multiply
- // would get unrolled. We handle it here.
- // It could underflow.
- //
- double t = dValue * TINY_10_POW[j];
- if (t == 0.0) {
- //
- // It did underflow.
- // Look more closely at the result.
- // If the exponent is just one too small,
- // then use the minimum finite as our estimate
- // value. Else call the result 0.0
- // and punt it.
- // ( I presume this could happen because
- // rounding forces the result here to be
- // an ULP or two less than
- // Double.MIN_VALUE ).
- //
- t = dValue * 2.0;
- t *= TINY_10_POW[j];
- if (t == 0.0) {
- return (isNegative) ? -0.0 : 0.0;
- }
- t = Double.MIN_VALUE;
- }
- dValue = t;
- }
- }
-
- //
- // dValue is now approximately the result.
- // The hard part is adjusting it, by comparison
- // with FDBigInteger arithmetic.
- // Formulate the EXACT big-number result as
- // bigD0 * 10^exp
- //
- if (nDigits > MAX_NDIGITS) {
- nDigits = MAX_NDIGITS + 1;
- digits[MAX_NDIGITS] = '1';
- }
- FDBigInteger bigD0 = new FDBigInteger(lValue, digits, kDigits, nDigits);
- exp = decExponent - nDigits;
-
- long ieeeBits = Double.doubleToRawLongBits(dValue); // IEEE-754 bits of double candidate
- final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop
- final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop
- bigD0 = bigD0.multByPow52(D5, 0);
- bigD0.makeImmutable(); // prevent bigD0 modification inside correctionLoop
- FDBigInteger bigD = null;
- int prevD2 = 0;
-
- correctionLoop:
- while (true) {
- // here ieeeBits can't be NaN, Infinity or zero
- int binexp = (int) (ieeeBits >>> EXP_SHIFT);
- long bigBbits = ieeeBits & DoubleConsts.SIGNIF_BIT_MASK;
- if (binexp > 0) {
- bigBbits |= FRACT_HOB;
- } else { // Normalize denormalized numbers.
- assert bigBbits != 0L : bigBbits; // doubleToBigInt(0.0)
- int leadingZeros = Long.numberOfLeadingZeros(bigBbits);
- int shift = leadingZeros - (63 - EXP_SHIFT);
- bigBbits <<= shift;
- binexp = 1 - shift;
- }
- binexp -= DoubleConsts.EXP_BIAS;
- int lowOrderZeros = Long.numberOfTrailingZeros(bigBbits);
- bigBbits >>>= lowOrderZeros;
- final int bigIntExp = binexp - EXP_SHIFT + lowOrderZeros;
- final int bigIntNBits = EXP_SHIFT + 1 - lowOrderZeros;
-
- //
- // Scale bigD, bigB appropriately for
- // big-integer operations.
- // Naively, we multiply by powers of ten
- // and powers of two. What we actually do
- // is keep track of the powers of 5 and
- // powers of 2 we would use, then factor out
- // common divisors before doing the work.
- //
- int B2 = B5; // powers of 2 in bigB
- int D2 = D5; // powers of 2 in bigD
- int Ulp2; // powers of 2 in halfUlp.
- if (bigIntExp >= 0) {
- B2 += bigIntExp;
- } else {
- D2 -= bigIntExp;
- }
- Ulp2 = B2;
- // shift bigB and bigD left by a number s. t.
- // halfUlp is still an integer.
- int hulpbias;
- if (binexp <= -DoubleConsts.EXP_BIAS) {
- // This is going to be a denormalized number
- // (if not actually zero).
- // half an ULP is at 2^-(DoubleConsts.EXP_BIAS+EXP_SHIFT+1)
- hulpbias = binexp + lowOrderZeros + DoubleConsts.EXP_BIAS;
- } else {
- hulpbias = 1 + lowOrderZeros;
- }
- B2 += hulpbias;
- D2 += hulpbias;
- // if there are common factors of 2, we might just as well
- // factor them out, as they add nothing useful.
- int common2 = Math.min(B2, Math.min(D2, Ulp2));
- B2 -= common2;
- D2 -= common2;
- Ulp2 -= common2;
- // do multiplications by powers of 5 and 2
- FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2);
- if (bigD == null || prevD2 != D2) {
- bigD = bigD0.leftShift(D2);
- prevD2 = D2;
- }
- //
- // to recap:
- // bigB is the scaled-big-int version of our floating-point
- // candidate.
- // bigD is the scaled-big-int version of the exact value
- // as we understand it.
- // halfUlp is 1/2 an ulp of bigB, except for special cases
- // of exact powers of 2
- //
- // the plan is to compare bigB with bigD, and if the difference
- // is less than halfUlp, then we're satisfied. Otherwise,
- // use the ratio of difference to halfUlp to calculate a fudge
- // factor to add to the floating value, then go 'round again.
- //
- FDBigInteger diff;
- int cmpResult;
- boolean overvalue;
- if ((cmpResult = bigB.cmp(bigD)) > 0) {
- overvalue = true; // our candidate is too big.
- diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse
- if ((bigIntNBits == 1) && (bigIntExp > -DoubleConsts.EXP_BIAS + 1)) {
- // candidate is a normalized exact power of 2 and
- // is too big (larger than Double.MIN_NORMAL). We will be subtracting.
- // For our purposes, ulp is the ulp of the
- // next smaller range.
- Ulp2 -= 1;
- if (Ulp2 < 0) {
- // rats. Cannot de-scale ulp this far.
- // must scale diff in other direction.
- Ulp2 = 0;
- diff = diff.leftShift(1);
- }
- }
- } else if (cmpResult < 0) {
- overvalue = false; // our candidate is too small.
- diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse
- } else {
- // the candidate is exactly right!
- // this happens with surprising frequency
- break correctionLoop;
- }
- cmpResult = diff.cmpPow52(B5, Ulp2);
- if ((cmpResult) < 0) {
- // difference is small.
- // this is close enough
- break correctionLoop;
- } else if (cmpResult == 0) {
- // difference is exactly half an ULP
- // round to some other value maybe, then finish
- if ((ieeeBits & 1) != 0) { // half ties to even
- ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
- }
- break correctionLoop;
- } else {
- // difference is non-trivial.
- // could scale addend by ratio of difference to
- // halfUlp here, if we bothered to compute that difference.
- // Most of the time ( I hope ) it is about 1 anyway.
- ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
- if (ieeeBits == 0 || ieeeBits == DoubleConsts.EXP_BIT_MASK) { // 0.0 or Double.POSITIVE_INFINITY
- break correctionLoop; // oops. Fell off end of range.
- }
- continue; // try again.
- }
-
- }
- if (isNegative) {
- ieeeBits |= DoubleConsts.SIGN_BIT_MASK;
- }
- return Double.longBitsToDouble(ieeeBits);
+ private double signed(double v) {
+ return isNegative ? -v : v;
}
- /**
- * Takes a FloatingDecimal, which we presumably just scanned in,
- * and finds out what its value is, as a float.
- * This is distinct from doubleValue() to avoid the extremely
- * unlikely case of a double rounding error, wherein the conversion
- * to double has one rounding error, and the conversion of that double
- * to a float has another rounding error, IN THE WRONG DIRECTION,
- * ( because of the preference to a zero low-order bit ).
- */
- @Override
- public float floatValue() {
- int kDigits = Math.min(nDigits, SINGLE_MAX_DECIMAL_DIGITS + 1);
- //
- // convert the lead kDigits to an integer.
- //
- int iValue = (int) digits[0] - (int) '0';
- for (int i = 1; i < kDigits; i++) {
- iValue = iValue * 10 + (int) digits[i] - (int) '0';
+ private float floatValue() {
+ /* For details not covered here, see comments in doubleValue(). */
+ if (e <= E_THR_Z[BINARY_32_IX]) {
+ return signed(0.0f);
}
- float fValue = (float) iValue;
- int exp = decExponent - kDigits;
- //
- // iValue now contains an integer with the value of
- // the first kDigits digits of the number.
- // fValue contains the (float) of the same.
- //
+ if (e >= E_THR_I[BINARY_32_IX]) {
+ return signed(Float.POSITIVE_INFINITY);
+ }
+ int n = this.n;
+ int ep = e - n;
+ float v;
+ int m = Math.min(n, MathUtils.N);
+ long fl = toLong(m);
+ if (n <= MathUtils.N && 0 <= ep && e <= MathUtils.N) {
+ fl *= MathUtils.pow10(ep); // 0 ≤ ep < 19
+ v = fl >= 0 ? fl : 2.0f * (fl >>> 1 | fl & 0b1);
+ return signed(v);
+ }
+ if (n <= FLOG_10_MAX_LONG && -SINGLE_MAX_SMALL_TEN <= ep) {
+ v = fl;
+ boolean isExact = (long) v == fl;
+ if (isExact && ep <= SINGLE_MAX_SMALL_TEN) {
+ v = ep >= 0 ? v * SINGLE_SMALL_10_POW[ep] : v / SINGLE_SMALL_10_POW[-ep];
+ return signed(v);
+ }
+ /*
+ * The similar case in doubleValue() where fl is exact and
+ * ep is somewhat larger than MAX_SMALL_TEN is already covered
+ * above for float.
+ */
+ }
+ long ll = fl;
+ long lh;
+ if (n <= MathUtils.N) {
+ ll *= MathUtils.pow10(MathUtils.N - n);
+ lh = ll;
+ } else {
+ lh = ll + 1;
+ }
+ int el = e - MathUtils.N;
+ int rp = MathUtils.flog2pow10(el) + 2;
+ long g1 = MathUtils.g1(el);
+ long nl1 = Math.unsignedMultiplyHigh(ll, g1);
+ long nl0 = ll * g1;
+ long nh1 = Math.unsignedMultiplyHigh(lh, g1 + 1);
+ long nh0 = lh * (g1 + 1);
+ int bl = Long.SIZE - Long.numberOfLeadingZeros(nl1);
+ if (bl + rp > Float.MIN_EXPONENT) {
+ float ul = nl1 | (nl0 != 0 ? 1 : 0);
+ float uh = nh1 | (nh0 != 0 ? 1 : 0);
+ v = Math.scalb(ul, rp);
+ if (ul == uh || v == Float.POSITIVE_INFINITY) {
+ return signed(v);
+ }
+ } else {
+ int bh = Long.SIZE - Long.numberOfLeadingZeros(nh1);
+ if (bh + rp <= Float.MIN_EXPONENT) {
+ int sh = FloatToDecimal.Q_MIN - rp;
+ long sbMask = -1L >>> 1 - sh;
- if (nDigits <= SINGLE_MAX_DECIMAL_DIGITS) {
- //
- // possibly an easy case.
- // We know that the digits can be represented
- // exactly. And if the exponent isn't too outrageous,
- // the whole thing can be done with one operation,
- // thus one rounding error.
- // Note that all our constructors trim all leading and
- // trailing zeros, so simple values (including zero)
- // will always end up here.
- //
- if (exp == 0 || fValue == 0.0f) {
- return (isNegative) ? -fValue : fValue; // small floating integer
- } else if (exp >= 0) {
- if (exp <= SINGLE_MAX_SMALL_TEN) {
- //
- // Can get the answer with one operation,
- // thus one roundoff.
- //
- fValue *= SINGLE_SMALL_10_POW[exp];
- return (isNegative) ? -fValue : fValue;
+ long nl1p = nl1 >>> sh;
+ long rb = nl1 >>> sh - 1;
+ long sb = (nl1 & sbMask | nl0) != 0 ? 1 : 0;
+ long corr = rb & (sb | nl1p) & 0b1;
+ float ul = nl1p + corr;
+
+ long nh1p = nh1 >>> sh;
+ rb = nh1 >>> sh - 1;
+ sb = (nh1 & sbMask | nh0) != 0 ? 1 : 0;
+ corr = rb & (sb | nh1p) & 0b1;
+ float uh = nh1p + corr;
+ v = Math.scalb(ul, rp + sh);
+ if (ul == uh) {
+ return signed(v);
}
- int slop = SINGLE_MAX_DECIMAL_DIGITS - kDigits;
- if (exp <= SINGLE_MAX_SMALL_TEN + slop) {
- //
- // We can multiply fValue by 10^(slop)
- // and it is still "small" and exact.
- // Then we can multiply by 10^(exp-slop)
- // with one rounding.
- //
- fValue *= SINGLE_SMALL_10_POW[slop];
- fValue *= SINGLE_SMALL_10_POW[exp - slop];
- return (isNegative) ? -fValue : fValue;
- }
- //
- // Else we have a hard case with a positive exp.
- //
} else {
- if (exp >= -SINGLE_MAX_SMALL_TEN) {
- //
- // Can get the answer in one division.
- //
- fValue /= SINGLE_SMALL_10_POW[-exp];
- return (isNegative) ? -fValue : fValue;
- }
- //
- // Else we have a hard case with a negative exp.
- //
- }
- } else if ((decExponent >= nDigits) && (nDigits + decExponent <= MAX_DECIMAL_DIGITS)) {
- //
- // In double-precision, this is an exact floating integer.
- // So we can compute to double, then shorten to float
- // with one round, and get the right answer.
- //
- // First, finish accumulating digits.
- // Then convert that integer to a double, multiply
- // by the appropriate power of ten, and convert to float.
- //
- long lValue = (long) iValue;
- for (int i = kDigits; i < nDigits; i++) {
- lValue = lValue * 10L + (long) ((int) digits[i] - (int) '0');
- }
- double dValue = (double) lValue;
- exp = decExponent - nDigits;
- dValue *= SMALL_10_POW[exp];
- fValue = (float) dValue;
- return (isNegative) ? -fValue : fValue;
-
- }
- //
- // Harder cases:
- // The sum of digits plus exponent is greater than
- // what we think we can do with one error.
- //
- // Start by approximating the right answer by,
- // naively, scaling by powers of 10.
- // Scaling uses doubles to avoid overflow/underflow.
- //
- double dValue = fValue;
- if (exp > 0) {
- if (decExponent > SINGLE_MAX_DECIMAL_EXPONENT + 1) {
- //
- // Lets face it. This is going to be
- // Infinity. Cut to the chase.
- //
- return (isNegative) ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY;
- }
- if ((exp & 15) != 0) {
- dValue *= SMALL_10_POW[exp & 15];
- }
- if ((exp >>= 4) != 0) {
- int j;
- for (j = 0; exp > 0; j++, exp >>= 1) {
- if ((exp & 1) != 0) {
- dValue *= BIG_10_POW[j];
- }
- }
- }
- } else if (exp < 0) {
- exp = -exp;
- if (decExponent < SINGLE_MIN_DECIMAL_EXPONENT - 1) {
- //
- // Lets face it. This is going to be
- // zero. Cut to the chase.
- //
- return (isNegative) ? -0.0f : 0.0f;
- }
- if ((exp & 15) != 0) {
- dValue /= SMALL_10_POW[exp & 15];
- }
- if ((exp >>= 4) != 0) {
- int j;
- for (j = 0; exp > 0; j++, exp >>= 1) {
- if ((exp & 1) != 0) {
- dValue *= TINY_10_POW[j];
- }
- }
+ return signed(Float.MIN_NORMAL);
}
}
- fValue = Math.clamp((float) dValue, Float.MIN_VALUE, Float.MAX_VALUE);
-
- //
- // fValue is now approximately the result.
- // The hard part is adjusting it, by comparison
- // with FDBigInteger arithmetic.
- // Formulate the EXACT big-number result as
- // bigD0 * 10^exp
- //
- if (nDigits > SINGLE_MAX_NDIGITS) {
- nDigits = SINGLE_MAX_NDIGITS + 1;
- digits[SINGLE_MAX_NDIGITS] = '1';
- }
- FDBigInteger bigD0 = new FDBigInteger(iValue, digits, kDigits, nDigits);
- exp = decExponent - nDigits;
-
- int ieeeBits = Float.floatToRawIntBits(fValue); // IEEE-754 bits of float candidate
- final int B5 = Math.max(0, -exp); // powers of 5 in bigB, value is not modified inside correctionLoop
- final int D5 = Math.max(0, exp); // powers of 5 in bigD, value is not modified inside correctionLoop
- bigD0 = bigD0.multByPow52(D5, 0);
- bigD0.makeImmutable(); // prevent bigD0 modification inside correctionLoop
- FDBigInteger bigD = null;
- int prevD2 = 0;
-
- correctionLoop:
- while (true) {
- // here ieeeBits can't be NaN, Infinity or zero
- int binexp = ieeeBits >>> SINGLE_EXP_SHIFT;
- int bigBbits = ieeeBits & FloatConsts.SIGNIF_BIT_MASK;
- if (binexp > 0) {
- bigBbits |= SINGLE_FRACT_HOB;
- } else { // Normalize denormalized numbers.
- assert bigBbits != 0 : bigBbits; // floatToBigInt(0.0)
- int leadingZeros = Integer.numberOfLeadingZeros(bigBbits);
- int shift = leadingZeros - (31 - SINGLE_EXP_SHIFT);
- bigBbits <<= shift;
- binexp = 1 - shift;
- }
- binexp -= FloatConsts.EXP_BIAS;
- int lowOrderZeros = Integer.numberOfTrailingZeros(bigBbits);
- bigBbits >>>= lowOrderZeros;
- final int bigIntExp = binexp - SINGLE_EXP_SHIFT + lowOrderZeros;
- final int bigIntNBits = SINGLE_EXP_SHIFT + 1 - lowOrderZeros;
-
- //
- // Scale bigD, bigB appropriately for
- // big-integer operations.
- // Naively, we multiply by powers of ten
- // and powers of two. What we actually do
- // is keep track of the powers of 5 and
- // powers of 2 we would use, then factor out
- // common divisors before doing the work.
- //
- int B2 = B5; // powers of 2 in bigB
- int D2 = D5; // powers of 2 in bigD
- int Ulp2; // powers of 2 in halfUlp.
- if (bigIntExp >= 0) {
- B2 += bigIntExp;
- } else {
- D2 -= bigIntExp;
- }
- Ulp2 = B2;
- // shift bigB and bigD left by a number s. t.
- // halfUlp is still an integer.
- int hulpbias;
- if (binexp <= -FloatConsts.EXP_BIAS) {
- // This is going to be a denormalized number
- // (if not actually zero).
- // half an ULP is at 2^-(FloatConsts.EXP_BIAS+SINGLE_EXP_SHIFT+1)
- hulpbias = binexp + lowOrderZeros + FloatConsts.EXP_BIAS;
- } else {
- hulpbias = 1 + lowOrderZeros;
- }
- B2 += hulpbias;
- D2 += hulpbias;
- // if there are common factors of 2, we might just as well
- // factor them out, as they add nothing useful.
- int common2 = Math.min(B2, Math.min(D2, Ulp2));
- B2 -= common2;
- D2 -= common2;
- Ulp2 -= common2;
- // do multiplications by powers of 5 and 2
- FDBigInteger bigB = FDBigInteger.valueOfMulPow52(bigBbits, B5, B2);
- if (bigD == null || prevD2 != D2) {
- bigD = bigD0.leftShift(D2);
- prevD2 = D2;
- }
- //
- // to recap:
- // bigB is the scaled-big-int version of our floating-point
- // candidate.
- // bigD is the scaled-big-int version of the exact value
- // as we understand it.
- // halfUlp is 1/2 an ulp of bigB, except for special cases
- // of exact powers of 2
- //
- // the plan is to compare bigB with bigD, and if the difference
- // is less than halfUlp, then we're satisfied. Otherwise,
- // use the ratio of difference to halfUlp to calculate a fudge
- // factor to add to the floating value, then go 'round again.
- //
- FDBigInteger diff;
- int cmpResult;
- boolean overvalue;
- if ((cmpResult = bigB.cmp(bigD)) > 0) {
- overvalue = true; // our candidate is too big.
- diff = bigB.leftInplaceSub(bigD); // bigB is not user further - reuse
- if ((bigIntNBits == 1) && (bigIntExp > -FloatConsts.EXP_BIAS + 1)) {
- // candidate is a normalized exact power of 2 and
- // is too big (larger than Float.MIN_NORMAL). We will be subtracting.
- // For our purposes, ulp is the ulp of the
- // next smaller range.
- Ulp2 -= 1;
- if (Ulp2 < 0) {
- // rats. Cannot de-scale ulp this far.
- // must scale diff in other direction.
- Ulp2 = 0;
- diff = diff.leftShift(1);
- }
- }
- } else if (cmpResult < 0) {
- overvalue = false; // our candidate is too small.
- diff = bigD.rightInplaceSub(bigB); // bigB is not user further - reuse
- } else {
- // the candidate is exactly right!
- // this happens with surprising frequency
- break correctionLoop;
- }
- cmpResult = diff.cmpPow52(B5, Ulp2);
- if ((cmpResult) < 0) {
- // difference is small.
- // this is close enough
- break correctionLoop;
- } else if (cmpResult == 0) {
- // difference is exactly half an ULP
- // round to some other value maybe, then finish
- if ((ieeeBits & 1) != 0) { // half ties to even
- ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
- }
- break correctionLoop;
- } else {
- // difference is non-trivial.
- // could scale addend by ratio of difference to
- // halfUlp here, if we bothered to compute that difference.
- // Most of the time ( I hope ) it is about 1 anyway.
- ieeeBits += overvalue ? -1 : 1; // nextDown or nextUp
- if (ieeeBits == 0 || ieeeBits == FloatConsts.EXP_BIT_MASK) { // 0.0 or Float.POSITIVE_INFINITY
- break correctionLoop; // oops. Fell off end of range.
- }
- continue; // try again.
- }
-
- }
- if (isNegative) {
- ieeeBits |= FloatConsts.SIGN_BIT_MASK;
- }
- return Float.intBitsToFloat(ieeeBits);
+ int bits = Float.floatToRawIntBits(v);
+ int be = (bits & FloatConsts.EXP_BIT_MASK) >>> FloatConsts.SIGNIFICAND_WIDTH - 1;
+ int qr = be - (FloatConsts.EXP_BIAS + FloatConsts.SIGNIFICAND_WIDTH - 1)
+ - (be != 0 ? 1 : 0);
+ int cr = 2 * (bits & FloatConsts.SIGNIF_BIT_MASK | (be != 0 ? FloatToDecimal.C_MIN : 0)) + 1;
+ FDBigInteger lhs = valueOfMulPow52(cr, Math.max(-ep, 0), Math.max(qr - ep, 0));
+ FDBigInteger rhs = new FDBigInteger(fl, d, m, n)
+ .multByPow52(Math.max(ep, 0), Math.max(ep - qr, 0));
+ int cmp = lhs.cmp(rhs);
+ v = Float.intBitsToFloat(cmp < 0
+ ? bits + 1
+ : cmp > 0
+ ? bits
+ : bits + (bits & 0b1));
+ return signed(v);
}
+ private float signed(float v) {
+ return isNegative ? -v : v;
+ }
- /**
- * All the positive powers of 10 that can be
- * represented exactly in double/float.
- */
+ /* All the powers of 10 that can be represented exactly in double. */
+ @Stable
private static final double[] SMALL_10_POW = {
- 1.0e0,
- 1.0e1, 1.0e2, 1.0e3, 1.0e4, 1.0e5,
- 1.0e6, 1.0e7, 1.0e8, 1.0e9, 1.0e10,
- 1.0e11, 1.0e12, 1.0e13, 1.0e14, 1.0e15,
- 1.0e16, 1.0e17, 1.0e18, 1.0e19, 1.0e20,
- 1.0e21, 1.0e22
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
+ 1e20, 1e21, 1e22,
};
+ /* All the powers of 10 that can be represented exactly in float. */
+ @Stable
private static final float[] SINGLE_SMALL_10_POW = {
- 1.0e0f,
- 1.0e1f, 1.0e2f, 1.0e3f, 1.0e4f, 1.0e5f,
- 1.0e6f, 1.0e7f, 1.0e8f, 1.0e9f, 1.0e10f
+ 1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f, 1e6f, 1e7f, 1e8f, 1e9f,
+ 1e10f,
};
- private static final double[] BIG_10_POW = {
- 1e16, 1e32, 1e64, 1e128, 1e256 };
- private static final double[] TINY_10_POW = {
- 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 };
-
- private static final int MAX_SMALL_TEN = SMALL_10_POW.length-1;
- private static final int SINGLE_MAX_SMALL_TEN = SINGLE_SMALL_10_POW.length-1;
+ private static final int MAX_SMALL_TEN = SMALL_10_POW.length - 1;
+ private static final int SINGLE_MAX_SMALL_TEN = SINGLE_SMALL_10_POW.length - 1;
}
@@ -1508,13 +1204,13 @@ public class FloatingDecimal{
* @param compat compatibility with releases < JDK 21
* @return The converter.
*/
- public static BinaryToASCIIConverter getBinaryToASCIIConverter(double d, boolean compat) {
+ public static BinaryToASCIIBuffer getBinaryToASCIIConverter(double d, boolean compat) {
return compat
- ? getCompatBinaryToASCIIConverter(d, true)
+ ? getCompatBinaryToASCIIConverter(d)
: getBinaryToASCIIConverter(d);
}
- private static BinaryToASCIIConverter getBinaryToASCIIConverter(double d) {
+ private static BinaryToASCIIBuffer getBinaryToASCIIConverter(double d) {
assert Double.isFinite(d);
FormattedFPDecimal dec = FormattedFPDecimal.split(d);
@@ -1541,7 +1237,7 @@ public class FloatingDecimal{
* Should be removed in the future, along with its dependent methods and
* fields (> 550 lines).
*/
- private static BinaryToASCIIConverter getCompatBinaryToASCIIConverter(double d, boolean isCompatibleFormat) {
+ private static BinaryToASCIIBuffer getCompatBinaryToASCIIConverter(double d) {
long dBits = Double.doubleToRawLongBits(d);
boolean isNegative = (dBits&DoubleConsts.SIGN_BIT_MASK) != 0; // discover sign
long fractBits = dBits & DoubleConsts.SIGNIF_BIT_MASK;
@@ -1575,34 +1271,23 @@ public class FloatingDecimal{
}
binExp -= DoubleConsts.EXP_BIAS;
BinaryToASCIIBuffer buf = getBinaryToASCIIBuffer();
- buf.setSign(isNegative);
// call the routine that actually does all the hard work.
- buf.dtoa(binExp, fractBits, nSignificantBits, isCompatibleFormat);
+ buf.dtoa(binExp, fractBits, nSignificantBits);
return buf;
}
- static ASCIIToBinaryConverter readDoubleSignlessDigits(int decExp, byte[] digits, int length) {
-
- // Prevent an extreme negative exponent from causing overflow issues in doubleValue().
- // Large positive values are handled within doubleValue();
- if (decExp < MIN_DECIMAL_EXPONENT) {
- return A2BC_POSITIVE_ZERO;
- }
- return new ASCIIToBinaryBuffer(false, decExp, digits, length);
- }
-
/**
* The input must match the {@link Double#valueOf(String) rules described here},
* about leading and trailing whitespaces, and the grammar.
*
* @param in the non-null input
* @param ix one of the {@code BINARY__IX} constants, where {@code }
- * is one of 16, 32, 64
+ * is one of 16, 32, 64
* @return an appropriate binary converter
- * @throws NullPointerException if the input is null
+ * @throws NullPointerException if the input is null
* @throws NumberFormatException if the input is malformed
*/
- static ASCIIToBinaryConverter readJavaFormatString(String in, int ix) {
+ private static double readJavaFormatString(String in, int ix) {
/*
* The scanning proper does not allocate any object,
* nor does it perform any costly computation.
@@ -1624,6 +1309,7 @@ public class FloatingDecimal{
* 23 for BINARY_16_IX (Float16, once integrated in java.base)
* 114 for BINARY_32_IX (float)
* 769 for BINARY_64_IX (double)
+ * but is much shorter in common cases.
*/
int len = in.length(); // fail fast on null
@@ -1647,11 +1333,11 @@ public class FloatingDecimal{
ch = in.charAt(i);
if (ch == 'I') {
scanSymbolic(in, i, INFINITY_REP);
- return ssign != '-' ? A2BC_POSITIVE_INFINITY : A2BC_NEGATIVE_INFINITY;
+ return ssign != '-' ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
}
if (ch == 'N') {
scanSymbolic(in, i, NAN_REP);
- return A2BC_NOT_A_NUMBER; // ignore sign
+ return Double.NaN; // ignore sign
}
if (ch == '0' && i + 1 < len && toLowerCase(in.charAt(i + 1)) == 'x') {
isDec = false;
@@ -1730,7 +1416,7 @@ public class FloatingDecimal{
/* By now, the input is syntactically correct. */
if (tnz == 0) { // all zero digits, so ignore ep and point
- return ssign != '-' ? A2BC_POSITIVE_ZERO : A2BC_NEGATIVE_ZERO;
+ return ssign != '-' ? 0.0 : -0.0;
}
/*
@@ -1785,7 +1471,7 @@ public class FloatingDecimal{
ep += emult * (pt - tnz);
if (pt > tnz) { // '.' was counted as a position, adjust ep
ep -= emult;
- } else if (lz < pt) { // lz < pt <= tnz
+ } else if (lz < pt) { // lz < pt ≤ tnz
n -= 1;
}
}
@@ -1797,8 +1483,8 @@ public class FloatingDecimal{
* The magnitude x of the input meets
* x = f 10^ep (decimal)
* x = f 2^ep (hexadecimal)
- * Integer f = consists of the n decimal or hexadecimal
- * digits found in part [lz, tnz) of the input, and f_1 != 0, f_n != 0.
+ * Integer f = consists of the n decimal or hexadecimal
+ * digits found in part [lz, tnz) of the input, and d_1 > 0, d_n > 0.
*/
if (!isDec) { // hexadecimal conversion is performed entirely here
@@ -1824,19 +1510,19 @@ public class FloatingDecimal{
int bl = Long.SIZE - Long.numberOfLeadingZeros(c); // bitlength
/*
- * Let x = c 2^ep, so 2^(ep+bl-1) <= x < 2^(ep+bl)
+ * Let x = c 2^ep, so 2^(ep+bl-1) ≤ x < 2^(ep+bl)
* When ep + bl < Q_MIN then x certainly rounds to zero.
* When ep + bl > QE_MAX then x surely rounds to infinity.
*/
if (ep < Q_MIN[ix] - bl) {
- return ssign != '-' ? A2BC_POSITIVE_ZERO : A2BC_NEGATIVE_ZERO;
+ return ssign != '-' ? 0.0 : -0.0;
}
- if (ep > QE_MAX[ix] - bl) {
- return ssign != '-' ? A2BC_POSITIVE_INFINITY : A2BC_NEGATIVE_INFINITY;
+ if (ep > QP_MAX[ix] - bl) {
+ return ssign != '-' ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
}
int q = (int) ep; // narrowing conversion is safe
int shr; // (sh)ift to (r)ight iff shr > 0
- if (q >= QE_MIN[ix] - bl) {
+ if (q >= QP_MIN[ix] - bl) {
shr = bl - P[ix];
q += shr;
} else {
@@ -1860,10 +1546,8 @@ public class FloatingDecimal{
/* For now throw on BINARY_16_IX, until Float16 is integrated in java.base. */
return switch (ix) {
- case BINARY_32_IX ->
- new PreparedASCIIToBinaryBuffer(Double.NaN, buildFloat(ssign, q, c));
- case BINARY_64_IX ->
- new PreparedASCIIToBinaryBuffer(buildDouble(ssign, q, c), Float.NaN);
+ case BINARY_32_IX -> buildFloat(ssign, q, c);
+ case BINARY_64_IX -> buildDouble(ssign, q, c);
default -> throw new AssertionError("unexpected");
};
}
@@ -1873,27 +1557,27 @@ public class FloatingDecimal{
* rely on another method to do the (sometimes intensive) math conversion.
*
* Define e = n + ep, which leads to
- * x = 0.d_1 ... d_n 10^e, 10^(e-1) <= x < 10^e
- * If e <= E_THR_Z then x rounds to zero.
- * Similarly, if e >= E_THR_I then x rounds to infinity.
+ * x = 0.d_1 ... d_n 10^e, 10^(e-1) ≤ x < 10^e
+ * If e ≤ E_THR_Z then x rounds to zero.
+ * Similarly, if e ≥ E_THR_I then x rounds to infinity.
* We return immediately in these cases.
* Otherwise, e fits in an int, aptly named e as well.
*/
int e = Math.clamp(ep + n, E_THR_Z[ix], E_THR_I[ix]);
- if (e == E_THR_Z[ix]) { // true e <= E_THR_Z
- return ssign != '-' ? A2BC_POSITIVE_ZERO : A2BC_NEGATIVE_ZERO;
+ if (e == E_THR_Z[ix]) { // the true mathematical e ≤ E_THR_Z
+ return ssign != '-' ? 0.0 : -0.0;
}
- if (e == E_THR_I[ix]) { // true e >= E_THR_I
- return ssign != '-' ? A2BC_POSITIVE_INFINITY : A2BC_NEGATIVE_INFINITY;
+ if (e == E_THR_I[ix]) { // the true mathematical e ≥ E_THR_I
+ return ssign != '-' ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
}
/*
* For further considerations, x also needs to be seen as
* x = beta 2^q
* with real beta and integer q meeting
- * q >= Q_MIN
+ * q ≥ Q_MIN
* and
- * either 2^(P-1) <= beta < 2^P
+ * either 2^(P-1) ≤ beta < 2^P
* or 0 < beta < 2^(P-1) and q = Q_MIN
* The (unique) solution is
* q = max(floor(log2(x)) - (P-1), Q_MIN), beta = x 2^(-q)
@@ -1904,15 +1588,15 @@ public class FloatingDecimal{
* ql = max(floor((e-1) log2(10)) - (P-1), Q_MIN)
* qh = max(floor(e log2(10)) - (P-1), Q_MIN)
* then the following hold
- * ql <= q <= qh, and qh - ql <= 4
+ * ql ≤ q ≤ qh, and qh - ql ≤ 4
* Since by now e is relatively small, we can leverage flog2pow10().
*
* Consider the half-open interval [ 2^(P-1+q), 2^(P+q) ).
* It contains all floating-point values of the form
- * c 2^q, c integer, 2^(P-1) <= c < 2^P (normal values)
+ * c 2^q, c integer, 2^(P-1) ≤ c < 2^P (normal values)
* When q = Q_MIN also consider the interval half-open [0, 2^(P-1+q) ),
* which contains all floating-point values of the form
- * c 2^q, c integer, 0 <= c < 2^(P-1) (subnormal values and zero)
+ * c 2^q, c integer, 0 ≤ c < 2^(P-1) (subnormal values and zero)
* For these c values, all numbers of the form
* (c + 1/2) 2^q
* also belong to the intervals.
@@ -1927,12 +1611,12 @@ public class FloatingDecimal{
* (Well, the sticky digit is only needed when the integer part
* coincides with a boundary, but that's hard to detect at this stage.
* Adding the sticky digit is always safe.)
- * If n > e we pass the digits d_1...d_e 3 (3 is as good as any other
+ * If n > e we pass the digits (8 is as good as any other
* non-zero sticky digit) and the exponent e to the conversion routine.
- * If n <= e we pass all the digits d_1...d_n (no sticky digit,
+ * If n ≤ e we pass all the digits (no sticky digit,
* as the fractional part is empty) and the exponent e to the converter.
*
- * Now assume qh <= 0, so q <= 0.
+ * Now assume qh ≤ 0, so q ≤ 0.
* The boundaries (c + 1/2) 2^q = (2c + 1) 2^(q-1) have a fractional part
* of 1 - q digits: some (or zero) leading zeros, the rightmost is 5.
* A correct rounding needs to retain the integer part of x (if any),
@@ -1943,25 +1627,31 @@ public class FloatingDecimal{
* But let's keep it simple for now.)
* However, q is unknown, so use the conservative ql instead.
* More precisely, if n > e + 1 - ql we pass the leftmost e + 1 - ql
- * digits of f, sticky 3, and e.
- * Otherwise, n <= e + 1 - ql.
+ * digits of f, sticky 8 (the "most even" digit), and e.
+ * Otherwise, n ≤ e + 1 - ql.
* We pass all n digits of f, no sticky digit, and e to the converter.
*
- * Otherwise, ql <= 0 < qh, so -4 < q <= 4.
+ * Otherwise, ql ≤ 0 < qh, so -4 < q ≤ 4.
* Again, since q is not known exactly, we proceed as in the previous
* case, with ql as a safe replacement for q.
*/
- // TODO insert logic for small n: 9 for float, 18 for double
int ql = Math.max(MathUtils.flog2pow10(e - 1) - (P[ix] - 1), Q_MIN[ix]);
int np = e + Math.max(2 - ql, 1);
- byte[] digits = new byte[Math.min(n, np)];
+ byte[] d = new byte[Math.min(n, np)];
if (n >= np) {
- copyDigits(in, digits, np - 1, lz);
- digits[np - 1] = '3'; // append any non-zero sticky digit
+ copyDigits(in, d, np - 1, lz);
+ d[np - 1] = '8'; // append the "most even" non-zero sticky digit
} else {
- copyDigits(in, digits, n, lz);
+ copyDigits(in, d, n, lz);
}
- return new ASCIIToBinaryBuffer(ssign == '-', e, digits, digits.length);
+ /* For now throw on BINARY_16_IX, until Float16 is integrated in java.base. */
+ return switch (ix) {
+ case BINARY_32_IX ->
+ new ASCIIToBinaryBuffer(ssign == '-', e, d, d.length).floatValue();
+ case BINARY_64_IX ->
+ new ASCIIToBinaryBuffer(ssign == '-', e, d, d.length).doubleValue();
+ default -> throw new AssertionError("unexpected");
+ };
}
private static int toLowerCase(int ch) {
@@ -1988,17 +1678,17 @@ public class FloatingDecimal{
return Float.intBitsToFloat(bits);
}
- private static void copyDigits(String in, byte[] digits, int len, int i) {
+ private static void copyDigits(String in, byte[] d, int len, int i) {
int ch;
int j = 0;
while (j < len) {
if ((ch = in.charAt(i++)) != '.') {
- digits[j++] = (byte) ch;
+ d[j++] = (byte) ch;
}
}
}
- /* Arithmetically "appends the dec digit" ch to v >= 0, clamping at 10^10. */
+ /* Arithmetically "appends the dec digit" ch to v ≥ 0, clamping at 10^10. */
private static long appendDecDigit(long v, int ch) {
return v < 10_000_000_000L / 10 ? 10 * v + (ch - '0') : 10_000_000_000L;
}
@@ -2067,21 +1757,21 @@ public class FloatingDecimal{
* value of precision P is (uniquely) expressed as
* c 2^q
* where integers c and q meet
- * Q_MIN <= q <= Q_MAX
- * either 2^(P-1) <= c < 2^P (normal)
+ * Q_MIN ≤ q ≤ Q_MAX
+ * either 2^(P-1) ≤ c < 2^P (normal)
* or 0 < c < 2^(P-1) & q = Q_MIN (subnormal)
- * c = b_1...b_P (b_i in [0, 2))
+ * c = (b_i in [0, 2)), b_1 > 0 iff normal
*
* Equivalently, the floating-point value can be (uniquely) expressed as
- * m 2^ep
- * where integer qe and real f meet
- * qe = q + P
+ * m 2^qp
+ * where integer qp and real m meet
+ * qp = q + P
* m = c 2^(-P)
* Hence,
- * QE_MIN = Q_MIN + P, QE_MAX = Q_MAX + P,
- * 2^(-1) <= m < 1 (normal)
+ * QP_MIN = Q_MIN + P, QP_MAX = Q_MAX + P,
+ * 2^(-1) ≤ m < 1 (normal)
* m < 2^(-1) (subnormal)
- * m = 0.b_1...b_P
+ * m = <0.b_1...b_P>
*/
/*
@@ -2091,71 +1781,78 @@ public class FloatingDecimal{
private static final int BINARY_16_IX = 0;
private static final int BINARY_32_IX = 1;
private static final int BINARY_64_IX = 2;
-// private static final int BINARY_128_IX = 3;
-// private static final int BINARY_256_IX = 4;
+ // private static final int BINARY_128_IX = 3;
+ // private static final int BINARY_256_IX = 4;
@Stable
private static final int[] P = {
- 11,
- FloatToDecimal.P,
- DoubleToDecimal.P,
+ 11, // 11
+ FloatToDecimal.P, // 24
+ DoubleToDecimal.P, // 53
// 113,
// 237,
};
- @Stable
- private static final int[] W = {
- 5,
- FloatToDecimal.W,
- DoubleToDecimal.W,
-// (1 << 4 + BINARY_128_IX) - P[BINARY_128_IX],
-// (1 << 4 + BINARY_256_IX) - P[BINARY_256_IX],
- };
-
- /* Minimum exponent in the m 2^e representation. */
/* Minimum exponent in the c 2^q representation. */
@Stable
private static final int[] Q_MIN = {
- -24, // Float16ToDecimal.Q_MIN,
- FloatToDecimal.Q_MIN,
- DoubleToDecimal.Q_MIN,
-// QE_MIN[BINARY_128_IX] - (P[BINARY_128_IX] - 1),
-// QE_MIN[BINARY_256_IX] - (P[BINARY_256_IX] - 1),
+ -24, // Float16ToDecimal.Q_MIN, // -24
+ FloatToDecimal.Q_MIN, // -149
+ DoubleToDecimal.Q_MIN, // -1_074
+ // -16_494,
+ // -262_378,
};
+ /* Minimum exponent in the m 2^qp representation. */
@Stable
- private static final int[] QE_MIN = {
- Q_MIN[BINARY_16_IX] + P[BINARY_16_IX],
- FloatToDecimal.Q_MIN + FloatToDecimal.P,
- DoubleToDecimal.Q_MIN + DoubleToDecimal.P,
-// Q_MIN[BINARY_128_IX] + P[BINARY_128_IX],
-// Q_MIN[BINARY_256_IX] + P[BINARY_256_IX],
+ private static final int[] QP_MIN = {
+ Q_MIN[BINARY_16_IX] + P[BINARY_16_IX], // -13
+ Q_MIN[BINARY_32_IX] + P[BINARY_32_IX], // -125
+ Q_MIN[BINARY_64_IX] + P[BINARY_64_IX], // -1_021
+ // Q_MIN[BINARY_128_IX] + P[BINARY_128_IX], // -16_381
+ // Q_MIN[BINARY_256_IX] + P[BINARY_256_IX], // -262_141
};
- /* Maximum exponent in the m 2^e representation. */
+ /* Maximum exponent in the m 2^qp representation. */
@Stable
- private static final int[] QE_MAX = {
- 3 - QE_MIN[BINARY_16_IX],
- FloatToDecimal.Q_MAX + FloatToDecimal.P,
- DoubleToDecimal.Q_MAX + DoubleToDecimal.P,
-// 3 - QE_MIN[BINARY_128_IX],
-// 3 - QE_MIN[BINARY_256_IX],
+ private static final int[] QP_MAX = {
+ 3 - QP_MIN[BINARY_16_IX], // 16
+ 3 - QP_MIN[BINARY_32_IX], // 128
+ 3 - QP_MIN[BINARY_64_IX], // 1_024
+ // 3 - QP_MIN[BINARY_128_IX], // 16_384
+ // 3 - QP_MIN[BINARY_256_IX], // 262_144
};
+ /*
+ * For each binary floating-point format, let
+ * THR_Z = ulp(0.0) / 2 = MIN_VALUE / 2
+ * THR_Z is the zero threshold.
+ * Real x rounds to 0 by roundTiesToEven iff |x| ≤ THR_Z.
+ *
+ * E_THR_Z = max{e : 10^e ≤ THR_Z}.
+ */
@Stable
private static final int[] E_THR_Z = {
- -8,
- FloatToDecimal.E_THR_Z,
- DoubleToDecimal.E_THR_Z,
+ -8, // -8
+ FloatToDecimal.E_THR_Z, // -46
+ DoubleToDecimal.E_THR_Z, // -324
// -4_966,
// -78_985,
};
+ /*
+ * For each binary floating-point format, let
+ * THR_I = MAX_VALUE + ulp(MAX_VALUE) / 2
+ * THR_I is the infinity threshold.
+ * Real x rounds to infinity by roundTiesToEven iff |x| ≥ THR_I.
+ *
+ * E_THR_I = min{e : THR_I ≤ 10^(e-1)}.
+ */
@Stable
private static final int[] E_THR_I = {
- 6,
- FloatToDecimal.E_THR_I,
- DoubleToDecimal.E_THR_I,
+ 6, // 6
+ FloatToDecimal.E_THR_I, // 40
+ DoubleToDecimal.E_THR_I, // 310
// 4_934,
// 78_915,
};
@@ -2166,11 +1863,11 @@ public class FloatingDecimal{
*/
@Stable
private static final int[] HEX_COUNT = {
- P[BINARY_16_IX] / 4 + 2,
- P[BINARY_32_IX] / 4 + 2,
- P[BINARY_64_IX] / 4 + 2,
-// P[BINARY_128_IX] / 4 + 2,
-// P[BINARY_256_IX] / 4 + 2,
+ P[BINARY_16_IX] / 4 + 2, // 4
+ P[BINARY_32_IX] / 4 + 2, // 8
+ P[BINARY_64_IX] / 4 + 2, // 15
+ // P[BINARY_128_IX] / 4 + 2, // 30
+ // P[BINARY_256_IX] / 4 + 2, // 61
};
}
diff --git a/src/java.base/share/classes/jdk/internal/math/MathUtils.java b/src/java.base/share/classes/jdk/internal/math/MathUtils.java
index 172090facc0..259ab5daf9e 100644
--- a/src/java.base/share/classes/jdk/internal/math/MathUtils.java
+++ b/src/java.base/share/classes/jdk/internal/math/MathUtils.java
@@ -43,7 +43,7 @@ final class MathUtils {
*/
/*
- * N = max{n : 10^n - 1 <= 2^Long.SIZE - 1}
+ * N = max{n : 10^n - 1 ≤ 2^Long.SIZE - 1}
* Every positive integer up to N decimal digits fits in an unsigned long,
* but there are positive integers of N + 1 digits that do not.
*/
@@ -52,16 +52,16 @@ final class MathUtils {
/*
* Given integer e, let 10^e = beta 2^r for the unique integer r and
* real beta meeting
- * 2^125 <= beta < 2^126.
+ * 2^125 ≤ beta < 2^126.
* The unique r and beta are
* r = floor(log2(10^e)) - 125, beta = 10^e 2^(-r)
* Let g = floor(beta) + 1.
* Thus,
- * (g - 1) 2^r <= 10^e < g 2^r
+ * (g - 1) 2^r ≤ 10^e < g 2^r
* Further, let
* g1 = floor(g 2^(-63)), g0 = g - g1 2^63
*
- * For e with GE_MIN <= e <= GE_MAX, the values g1 and g0 are available
+ * For e with GE_MIN ≤ e ≤ GE_MAX, the values g1 and g0 are available
* by invoking methods g1(e) and g0(e).
* In addition, r = flog2pow10(e) - 125.
*
@@ -73,17 +73,27 @@ final class MathUtils {
* discussed in [1].
* (See DoubleToDecimal.K_MIN and DoubleToDecimal.K_MAX.)
*
- * Let x = f 10^ep, where integers f and ep meet 10^(n-1) <= f < 10^n
+ * Let x = f 10^ep, where integers f and ep meet 10^(n-1) ≤ f < 10^n
* (that is, f has n digits) and E_THR_Z < ep + n < E_THR_I.
* (See DoubleToDecimal.E_THR_Z and DoubleToDecimal.E_THR_I.)
*
- * The decimal->double fast paths assume n <= N.
- * Thus, E_THR_Z - (N - 1) <= ep <= E_THR_I - 2, which means that
+ * The decimal->double fast paths assume n ≤ N.
+ * Thus, E_THR_Z - (N - 1) ≤ ep ≤ E_THR_I - 2, which means that
* the interval [GE_MIN, GE_MAX] must include the interval
* [E_THR_Z - (N - 1), E_THR_I - 2].
* That is,
* GE_MIN = min(-K_MAX, E_THR_Z - (N - 1))
* GE_MAX = max(-K_MIN, E_THR_I - 2)
+ *
+ * For the record, while the definition of g allows the case g = 2^126,
+ * the maximal g1 in the lookup table is 0x7FDD_E7F4_CA72_E30FL (e = -146),
+ * the minimal g1 is 0x4000_0000_0000_0000L (for e = 0),
+ * and the minimal g0 is 1.
+ * Hence, as easily verified, we always have
+ * 2^62 < g1 + 1 < 2^31 (2^32 - 1) < 2^63
+ * 2^125 < g = g1 2^63 + g0 < (g1 + 1) 2^63 < 2^94 (2^32 - 1) < 2^126
+ * We will assume these bounds observed by glancing at the lookup table,
+ * and use them liberally when so needed.
*/
static final int GE_MIN = -342;
static final int GE_MAX = 324;
diff --git a/test/jdk/java/lang/Double/ParseDouble.java b/test/jdk/java/lang/Double/ParseDouble.java
index c8f3443d772..b3c7c8717c1 100644
--- a/test/jdk/java/lang/Double/ParseDouble.java
+++ b/test/jdk/java/lang/Double/ParseDouble.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2025, 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
@@ -23,7 +23,8 @@
/*
* @test
- * @bug 4160406 4705734 4707389 4826774 4895911 4421494 6358355 7021568 7039369 4396272
+ * @bug 4160406 4705734 4707389 4826774 4895911 4421494 6358355 7021568
+ * 7039369 4396272 8366017
* @summary Test for Double.parseDouble method and acceptance regex
*/
@@ -759,6 +760,39 @@ public class ParseDouble {
throw new RuntimeException("Inconsistent conversion");
}
+ private static void testFastPaths() {
+ /* Exercises the fast paths in jdk.internal.math.FloatingDecimal. */
+ check("1", 0x1.0p0); // 1.0
+ check("2.34000e2", 0x1.d4p7); // 234.0
+ check("9.223e18", 0x1.fffab689adb6p62); // 9.223e18
+ check("9.876e18", 0x1.121d33597384p63); // 9.876e18
+ check("9223372036854776833", 0x1.0000000000001p63); // 9.223372036854778E18
+ check("9223372036854776832", 0x1.0p63); // 9.223372036854776E18
+
+ check("1.23", 0x1.3ae147ae147aep0); // 1.23
+ check("0.000234", 0x1.eabbcb1cc9646p-13); // 2.34E-4
+ check("3.45e23", 0x1.2439f32cbea41p78); // 3.45E23
+ check("576460752303423616e20", 0x1.5af1d78b58c41p125); // 5.764607523034236E37
+
+ check("1e37", 0x1.e17b84357691bp122); // 1.0E37
+ check("8999e34", 0x1.0ecdc63717fbdp126); // 8.999E37
+ check("0.9999e36", 0x1.8125c09cb78b7p119); // 9.999E35
+ check("0.9876e37", 0x1.db831933296cep122); // 9.876E36
+
+ check("1.2e-200", 0x1.d64af4cc52935p-665); // 1.2E-200
+ check("2.3e100", 0x1.507ed84d17a69p333); // 2.3E100
+ check("1.2000000000000000003e-200", 0x1.d64af4cc52935p-665); // 1.2E-200
+ check("2.3000000000000000004e100", 0x1.507ed84d17a69p333); // 2.3E100
+ check("5.249320425370670463e308", Double.POSITIVE_INFINITY);
+ check("5.2493204253706704633e308", Double.POSITIVE_INFINITY);
+
+ check("1.2e-320", 0x0.000000000097dp-1022); // 1.2E-320
+ check("1.2000000000000000003e-320", 0x0.000000000097dp-1022); // 1.2E-320
+
+ check("2.225073858507201383e-308", Double.MIN_NORMAL);
+ check("2.2250738585072013831e-308", Double.MIN_NORMAL);
+ }
+
public static void main(String[] args) throws Exception {
rudimentaryTest();
@@ -775,5 +809,8 @@ public class ParseDouble {
testSubnormalPowers();
testPowers();
testStrictness();
+
+ testFastPaths();
}
+
}
diff --git a/test/jdk/java/lang/Float/ParseFloat.java b/test/jdk/java/lang/Float/ParseFloat.java
index 2828db3ee84..04f03407113 100644
--- a/test/jdk/java/lang/Float/ParseFloat.java
+++ b/test/jdk/java/lang/Float/ParseFloat.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2025, 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
@@ -23,8 +23,10 @@
/*
* @test
- * @bug 4160406 4705734 4707389 6358355 7032154
- * @summary Tests for Float.parseFloat method
+ * @bug 4160406 4705734 4707389 6358355 7032154 8366017
+ * @summary Tests for Float.parseFloat method (use -DallRoundtrips=true
+ * to additionally check all non-negative, non-NaN float->String->float
+ * roundtrips)
*/
import java.math.BigDecimal;
@@ -315,6 +317,22 @@ public class ParseFloat {
check(new BigDecimal(Float.MAX_VALUE).add(new BigDecimal(Math.ulp(Float.MAX_VALUE)).multiply(HALF)).toString());
}
+ private static final int N = Float.floatToIntBits(Float.POSITIVE_INFINITY);
+
+ /* Tests all non-negative, non-NaN float->String->float roundtrips. */
+ private static void testAllRoundtrips() {
+ for (int i = 0; i <= N; ++i) {
+ float v = Float.intBitsToFloat(i);
+ String s = Float.toString(v);
+ float v0 = Float.parseFloat(s);
+ if (v != v0) {
+ fail(s, v0);
+ }
+ }
+ }
+
+ private static final String ALL_ROUNDTRIPS_PROP = "allRoundtrips";
+
public static void main(String[] args) throws Exception {
rudimentaryTest();
@@ -324,5 +342,9 @@ public class ParseFloat {
testParsing(paddedBadStrings, true);
testPowers();
+
+ if (Boolean.getBoolean(ALL_ROUNDTRIPS_PROP)) {
+ testAllRoundtrips();
+ }
}
}
diff --git a/test/jdk/jdk/internal/math/FloatingDecimal/TestFDBigInteger.java b/test/jdk/jdk/internal/math/FloatingDecimal/TestFDBigInteger.java
index 04be8efa38c..d2cdf9365e0 100644
--- a/test/jdk/jdk/internal/math/FloatingDecimal/TestFDBigInteger.java
+++ b/test/jdk/jdk/internal/math/FloatingDecimal/TestFDBigInteger.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2025, 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
@@ -21,415 +21,21 @@
* questions.
*/
-import java.math.BigInteger;
-import java.nio.charset.StandardCharsets;
-import jdk.internal.math.FDBigInteger;
+import jdk.internal.math.FDBigIntegerChecker;
-/**
+/*
* @test
* @bug 7032154 8342693
* @summary unit tests of FDBigInteger
* @modules java.base/jdk.internal.math
- * @author Dmitry Nadezhin
+ * @library java.base
+ * @build java.base/jdk.internal.math.*
+ * @run main TestFDBigInteger
*/
public class TestFDBigInteger {
- private static final int MAX_P5 = 413;
- private static final int MAX_P2 = 65;
- private static final long LONG_SIGN_MASK = (1L << 63);
- private static final BigInteger FIVE = BigInteger.valueOf(5);
- private static final FDBigInteger MUTABLE_ZERO = FDBigInteger.valueOfPow52(0, 0).leftInplaceSub(FDBigInteger.valueOfPow52(0, 0));
- private static final FDBigInteger IMMUTABLE_ZERO = FDBigInteger.valueOfPow52(0, 0).leftInplaceSub(FDBigInteger.valueOfPow52(0, 0));
- private static final FDBigInteger IMMUTABLE_MILLION = genMillion1();
- private static final FDBigInteger IMMUTABLE_BILLION = genBillion1();
- private static final FDBigInteger IMMUTABLE_TEN18 = genTen18();
-
- static {
- IMMUTABLE_ZERO.makeImmutable();
- IMMUTABLE_MILLION.makeImmutable();
- IMMUTABLE_BILLION.makeImmutable();
- IMMUTABLE_TEN18.makeImmutable();
- }
-
- private static FDBigInteger mutable(String hex, int offset) {
- byte[] chars = new BigInteger(hex, 16).toString().getBytes(StandardCharsets.US_ASCII);
- return new FDBigInteger(0, chars, 0, chars.length).multByPow52(0, offset * 32);
- }
-
- private static FDBigInteger immutable(String hex, int offset) {
- FDBigInteger fd = mutable(hex, offset);
- fd.makeImmutable();
- return fd;
- }
-
- private static BigInteger biPow52(int p5, int p2) {
- return FIVE.pow(p5).shiftLeft(p2);
- }
-
- // data.length == 1, nWords == 1, offset == 0
- private static FDBigInteger genMillion1() {
- return FDBigInteger.valueOfPow52(6, 0).leftShift(6);
- }
-
- // data.length == 2, nWords == 1, offset == 0
- private static FDBigInteger genMillion2() {
- return FDBigInteger.valueOfMulPow52(1000000L, 0, 0);
- }
-
- // data.length == 1, nWords == 1, offset == 0
- private static FDBigInteger genBillion1() {
- return FDBigInteger.valueOfPow52(9, 0).leftShift(9);
- }
-
- // data.length == 2, nWords == 2, offset == 0
- private static FDBigInteger genTen18() {
- return FDBigInteger.valueOfPow52(18, 0).leftShift(18);
- }
-
- private static void check(BigInteger expected, FDBigInteger actual, String message) throws Exception {
- if (!expected.equals(actual.toBigInteger())) {
- throw new Exception(message + " result " + actual.toHexString() + " expected " + expected.toString(16));
- }
- }
-
- private static void testValueOfPow52(int p5, int p2) throws Exception {
- check(biPow52(p5, p2), FDBigInteger.valueOfPow52(p5, p2),
- "valueOfPow52(" + p5 + "," + p2 + ")");
- }
-
- private static void testValueOfPow52() throws Exception {
- for (int p5 = 0; p5 <= MAX_P5; p5++) {
- for (int p2 = 0; p2 <= MAX_P2; p2++) {
- testValueOfPow52(p5, p2);
- }
- }
- }
-
- private static void testValueOfMulPow52(long value, int p5, int p2) throws Exception {
- BigInteger bi = BigInteger.valueOf(value & ~LONG_SIGN_MASK);
- if (value < 0) {
- bi = bi.setBit(63);
- }
- check(biPow52(p5, p2).multiply(bi), FDBigInteger.valueOfMulPow52(value, p5, p2),
- "valueOfMulPow52(" + Long.toHexString(value) + "." + p5 + "," + p2 + ")");
- }
-
- private static void testValueOfMulPow52(long value, int p5) throws Exception {
- testValueOfMulPow52(value, p5, 0);
- testValueOfMulPow52(value, p5, 1);
- testValueOfMulPow52(value, p5, 30);
- testValueOfMulPow52(value, p5, 31);
- testValueOfMulPow52(value, p5, 33);
- testValueOfMulPow52(value, p5, 63);
- }
-
- private static void testValueOfMulPow52() throws Exception {
- for (int p5 = 0; p5 <= MAX_P5; p5++) {
- testValueOfMulPow52(0xFFFFFFFFL, p5);
- testValueOfMulPow52(0x123456789AL, p5);
- testValueOfMulPow52(0x7FFFFFFFFFFFFFFFL, p5);
- testValueOfMulPow52(0xFFFFFFFFFFF54321L, p5);
- }
- }
-
- private static void testLeftShift(FDBigInteger t, int shift, boolean isImmutable) throws Exception {
- BigInteger bt = t.toBigInteger();
- FDBigInteger r = t.leftShift(shift);
- if ((bt.signum() == 0 || shift == 0 || !isImmutable) && r != t) {
- throw new Exception("leftShift doesn't reuse its argument");
- }
- if (isImmutable) {
- check(bt, t, "leftShift corrupts its argument");
- }
- check(bt.shiftLeft(shift), r, "leftShift returns wrong result");
- }
-
- private static void testLeftShift() throws Exception {
- testLeftShift(IMMUTABLE_ZERO, 0, true);
- testLeftShift(IMMUTABLE_ZERO, 10, true);
- testLeftShift(MUTABLE_ZERO, 0, false);
- testLeftShift(MUTABLE_ZERO, 10, false);
-
- testLeftShift(IMMUTABLE_MILLION, 0, true);
- testLeftShift(IMMUTABLE_MILLION, 1, true);
- testLeftShift(IMMUTABLE_MILLION, 12, true);
- testLeftShift(IMMUTABLE_MILLION, 13, true);
- testLeftShift(IMMUTABLE_MILLION, 32, true);
- testLeftShift(IMMUTABLE_MILLION, 33, true);
- testLeftShift(IMMUTABLE_MILLION, 44, true);
- testLeftShift(IMMUTABLE_MILLION, 45, true);
-
- testLeftShift(genMillion1(), 0, false);
- testLeftShift(genMillion1(), 1, false);
- testLeftShift(genMillion1(), 12, false);
- testLeftShift(genMillion1(), 13, false);
- testLeftShift(genMillion1(), 25, false);
- testLeftShift(genMillion1(), 26, false);
- testLeftShift(genMillion1(), 32, false);
- testLeftShift(genMillion1(), 33, false);
- testLeftShift(genMillion1(), 44, false);
- testLeftShift(genMillion1(), 45, false);
-
- testLeftShift(genMillion2(), 0, false);
- testLeftShift(genMillion2(), 1, false);
- testLeftShift(genMillion2(), 12, false);
- testLeftShift(genMillion2(), 13, false);
- testLeftShift(genMillion2(), 25, false);
- testLeftShift(genMillion2(), 26, false);
- testLeftShift(genMillion2(), 32, false);
- testLeftShift(genMillion2(), 33, false);
- testLeftShift(genMillion2(), 44, false);
- testLeftShift(genMillion2(), 45, false);
- }
-
- private static void testQuoRemIteration(FDBigInteger t, FDBigInteger s) throws Exception {
- BigInteger bt = t.toBigInteger();
- BigInteger bs = s.toBigInteger();
- int q = t.quoRemIteration(s);
- BigInteger[] qr = bt.divideAndRemainder(bs);
- if (!BigInteger.valueOf(q).equals(qr[0])) {
- throw new Exception("quoRemIteration returns incorrect quo");
- }
- check(qr[1].multiply(BigInteger.TEN), t, "quoRemIteration returns incorrect rem");
- }
-
- private static void testQuoRemIteration() throws Exception {
- // IMMUTABLE_TEN18 == 0de0b6b3a7640000
- // q = 0
- testQuoRemIteration(mutable("00000001", 0), IMMUTABLE_TEN18);
- testQuoRemIteration(mutable("00000001", 1), IMMUTABLE_TEN18);
- testQuoRemIteration(mutable("0de0b6b2", 1), IMMUTABLE_TEN18);
- // q = 1 -> q = 0
- testQuoRemIteration(mutable("0de0b6b3", 1), IMMUTABLE_TEN18);
- testQuoRemIteration(mutable("0de0b6b3a763FFFF", 0), IMMUTABLE_TEN18);
- // q = 1
- testQuoRemIteration(mutable("0de0b6b3a7640000", 0), IMMUTABLE_TEN18);
- testQuoRemIteration(mutable("0de0b6b3FFFFFFFF", 0), IMMUTABLE_TEN18);
- testQuoRemIteration(mutable("8ac72304", 1), IMMUTABLE_TEN18);
- testQuoRemIteration(mutable("0de0b6b400000000", 0), IMMUTABLE_TEN18);
- testQuoRemIteration(mutable("8ac72305", 1), IMMUTABLE_TEN18);
- // q = 18
- testQuoRemIteration(mutable("FFFFFFFF", 1), IMMUTABLE_TEN18);
- }
-
- private static void testCmp(FDBigInteger t, FDBigInteger o) throws Exception {
- BigInteger bt = t.toBigInteger();
- BigInteger bo = o.toBigInteger();
- int cmp = t.cmp(o);
- int bcmp = bt.compareTo(bo);
- if (bcmp != cmp) {
- throw new Exception("cmp returns " + cmp + " expected " + bcmp);
- }
- check(bt, t, "cmp corrupts this");
- check(bo, o, "cmp corrupts other");
- if (o.cmp(t) != -cmp) {
- throw new Exception("asymmetrical cmp");
- }
- check(bt, t, "cmp corrupts this");
- check(bo, o, "cmp corrupts other");
- }
-
- private static void testCmp() throws Exception {
- testCmp(mutable("FFFFFFFF", 0), mutable("100000000", 0));
- testCmp(mutable("FFFFFFFF", 0), mutable("1", 1));
- testCmp(mutable("5", 0), mutable("6", 0));
- testCmp(mutable("5", 0), mutable("5", 0));
- testCmp(mutable("5000000001", 0), mutable("500000001", 0));
- testCmp(mutable("5000000001", 0), mutable("6", 1));
- testCmp(mutable("5000000001", 0), mutable("5", 1));
- testCmp(mutable("5000000000", 0), mutable("5", 1));
- }
-
- private static void testCmpPow52(FDBigInteger t, int p5, int p2) throws Exception {
- FDBigInteger o = FDBigInteger.valueOfPow52(p5, p2);
- BigInteger bt = t.toBigInteger();
- BigInteger bo = biPow52(p5, p2);
- int cmp = t.cmp(o);
- int bcmp = bt.compareTo(bo);
- if (bcmp != cmp) {
- throw new Exception("cmpPow52 returns " + cmp + " expected " + bcmp);
- }
- check(bt, t, "cmpPow52 corrupts this");
- check(bo, o, "cmpPow5 corrupts other");
- }
-
- private static void testCmpPow52() throws Exception {
- testCmpPow52(mutable("00000002", 1), 0, 31);
- testCmpPow52(mutable("00000002", 1), 0, 32);
- testCmpPow52(mutable("00000002", 1), 0, 33);
- testCmpPow52(mutable("00000002", 1), 0, 34);
- testCmpPow52(mutable("00000002", 1), 0, 64);
- testCmpPow52(mutable("00000003", 1), 0, 32);
- testCmpPow52(mutable("00000003", 1), 0, 33);
- testCmpPow52(mutable("00000003", 1), 0, 34);
- }
-
- private static void testAddAndCmp(FDBigInteger t, FDBigInteger x, FDBigInteger y) throws Exception {
- BigInteger bt = t.toBigInteger();
- BigInteger bx = x.toBigInteger();
- BigInteger by = y.toBigInteger();
- int cmp = t.addAndCmp(x, y);
- int bcmp = bt.compareTo(bx.add(by));
- if (bcmp != cmp) {
- throw new Exception("addAndCmp returns " + cmp + " expected " + bcmp);
- }
- check(bt, t, "addAndCmp corrupts this");
- check(bx, x, "addAndCmp corrupts x");
- check(by, y, "addAndCmp corrupts y");
- }
-
- private static void testAddAndCmp() throws Exception {
- testAddAndCmp(MUTABLE_ZERO, MUTABLE_ZERO, MUTABLE_ZERO);
- testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, MUTABLE_ZERO);
- testAddAndCmp(mutable("00000001", 0), mutable("00000001", 0), MUTABLE_ZERO);
- testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000001", 0));
- testAddAndCmp(mutable("00000001", 0), mutable("00000002", 0), MUTABLE_ZERO);
- testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000002", 0));
- testAddAndCmp(mutable("00000001", 2), mutable("FFFFFFFF", 0), mutable("FFFFFFFF", 0));
- testAddAndCmp(mutable("00000001", 0), mutable("00000001", 1), mutable("00000001", 0));
-
- testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0F80000000", 1), mutable("F0F0F0F080000000", 1));
- testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0E80000000", 1), mutable("F0F0F0F080000000", 1));
-
- testAddAndCmp(mutable("00000002", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
- testAddAndCmp(mutable("00000003", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
- testAddAndCmp(mutable("00000004", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
- testAddAndCmp(mutable("00000005", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
-
- testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
- testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000001", 0));
- testAddAndCmp(mutable("00000002", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
- testAddAndCmp(mutable("00000003", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
- }
-
- private static void testMultBy10(FDBigInteger t, boolean isImmutable) throws Exception {
- BigInteger bt = t.toBigInteger();
- FDBigInteger r = t.multBy10();
- if ((bt.signum() == 0 || !isImmutable) && r != t) {
- throw new Exception("multBy10 of doesn't reuse its argument");
- }
- if (isImmutable) {
- check(bt, t, "multBy10 corrupts its argument");
- }
- check(bt.multiply(BigInteger.TEN), r, "multBy10 returns wrong result");
- }
-
- private static void testMultBy10() throws Exception {
- for (int p5 = 0; p5 <= MAX_P5; p5++) {
- for (int p2 = 0; p2 <= MAX_P2; p2++) {
- // This strange way of creating a value ensures that it is mutable.
- FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
- testMultBy10(value, false);
- value.makeImmutable();
- testMultBy10(value, true);
- }
- }
- }
-
- private static void testMultByPow52(FDBigInteger t, int p5, int p2) throws Exception {
- BigInteger bt = t.toBigInteger();
- FDBigInteger r = t.multByPow52(p5, p2);
- if (bt.signum() == 0 && r != t) {
- throw new Exception("multByPow52 of doesn't reuse its argument");
- }
- check(bt.multiply(biPow52(p5, p2)), r, "multByPow52 returns wrong result");
- }
-
- private static void testMultByPow52() throws Exception {
- for (int p5 = 0; p5 <= MAX_P5; p5++) {
- for (int p2 = 0; p2 <= MAX_P2; p2++) {
- // This strange way of creating a value ensures that it is mutable.
- FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
- testMultByPow52(value, p5, p2);
- }
- }
- }
-
- private static void testLeftInplaceSub(FDBigInteger left, FDBigInteger right, boolean isImmutable) throws Exception {
- BigInteger biLeft = left.toBigInteger();
- BigInteger biRight = right.toBigInteger();
- FDBigInteger diff = left.leftInplaceSub(right);
- if (!isImmutable && diff != left) {
- throw new Exception("leftInplaceSub of doesn't reuse its argument");
- }
- if (isImmutable) {
- check(biLeft, left, "leftInplaceSub corrupts its left immutable argument");
- }
- check(biRight, right, "leftInplaceSub corrupts its right argument");
- check(biLeft.subtract(biRight), diff, "leftInplaceSub returns wrong result");
- }
-
- private static void testLeftInplaceSub() throws Exception {
- for (int p5 = 0; p5 <= MAX_P5; p5++) {
- for (int p2 = 0; p2 <= MAX_P2; p2++) {
-// for (int p5r = 0; p5r <= p5; p5r += 10) {
-// for (int p2r = 0; p2r <= p2; p2r += 10) {
- for (int p5r = 0; p5r <= p5; p5r++) {
- for (int p2r = 0; p2r <= p2; p2r++) {
- // This strange way of creating a value ensures that it is mutable.
- FDBigInteger left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
- FDBigInteger right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r);
- testLeftInplaceSub(left, right, false);
- left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
- left.makeImmutable();
- testLeftInplaceSub(left, right, true);
- }
- }
- }
- }
- }
-
- private static void testRightInplaceSub(FDBigInteger left, FDBigInteger right, boolean isImmutable) throws Exception {
- BigInteger biLeft = left.toBigInteger();
- BigInteger biRight = right.toBigInteger();
- FDBigInteger diff = left.rightInplaceSub(right);
- if (!isImmutable && diff != right) {
- throw new Exception("rightInplaceSub of doesn't reuse its argument");
- }
- check(biLeft, left, "leftInplaceSub corrupts its left argument");
- if (isImmutable) {
- check(biRight, right, "leftInplaceSub corrupts its right immutable argument");
- }
- try {
- check(biLeft.subtract(biRight), diff, "rightInplaceSub returns wrong result");
- } catch (Exception e) {
- System.out.println(biLeft+" - "+biRight+" = "+biLeft.subtract(biRight));
- throw e;
- }
- }
-
- private static void testRightInplaceSub() throws Exception {
- for (int p5 = 0; p5 <= MAX_P5; p5++) {
- for (int p2 = 0; p2 <= MAX_P2; p2++) {
-// for (int p5r = 0; p5r <= p5; p5r += 10) {
-// for (int p2r = 0; p2r <= p2; p2r += 10) {
- for (int p5r = 0; p5r <= p5; p5r++) {
- for (int p2r = 0; p2r <= p2; p2r++) {
- // This strange way of creating a value ensures that it is mutable.
- FDBigInteger left = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
- FDBigInteger right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r);
- testRightInplaceSub(left, right, false);
- right = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5r, p2r);
- right.makeImmutable();
- testRightInplaceSub(left, right, true);
- }
- }
- }
- }
- }
-
public static void main(String[] args) throws Exception {
- testValueOfPow52();
- testValueOfMulPow52();
- testLeftShift();
- testQuoRemIteration();
- testCmp();
- testCmpPow52();
- testAddAndCmp();
- // Uncomment the following for more comprehensize but slow testing.
- // testLeftInplaceSub();
- // testMultBy10();
- // testMultByPow52();
- // testRightInplaceSub();
+ FDBigIntegerChecker.main(args);
}
+
}
diff --git a/test/jdk/jdk/internal/math/FloatingDecimal/java.base/jdk/internal/math/FDBigIntegerChecker.java b/test/jdk/jdk/internal/math/FloatingDecimal/java.base/jdk/internal/math/FDBigIntegerChecker.java
new file mode 100644
index 00000000000..3a52e597ead
--- /dev/null
+++ b/test/jdk/jdk/internal/math/FloatingDecimal/java.base/jdk/internal/math/FDBigIntegerChecker.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (c) 2013, 2025, 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.
+ *
+ * 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.internal.math;
+
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+
+public class FDBigIntegerChecker {
+
+ private static final int MAX_P5 = 413;
+ private static final int MAX_P2 = 65;
+ private static final long LONG_SIGN_MASK = (1L << 63);
+ private static final BigInteger FIVE = BigInteger.valueOf(5);
+ private static final FDBigInteger MUTABLE_ZERO = new FDBigInteger(0);
+ private static final FDBigInteger IMMUTABLE_ZERO = new FDBigInteger(0).makeImmutable();
+ private static final FDBigInteger IMMUTABLE_MILLION = genMillion1().makeImmutable();
+ private static final FDBigInteger IMMUTABLE_TEN18 = genTen18().makeImmutable();
+
+ private static BigInteger toBigInteger(FDBigInteger v) {
+ return new BigInteger(v.toByteArray());
+ }
+
+ private static FDBigInteger mutable(String hex, int offset) {
+ byte[] chars = new BigInteger(hex, 16).toString().getBytes(StandardCharsets.US_ASCII);
+ return new FDBigInteger(0, chars, 0, chars.length).multByPow52(0, offset * 32);
+ }
+
+ private static BigInteger biPow52(int p5, int p2) {
+ return FIVE.pow(p5).shiftLeft(p2);
+ }
+
+ // data.length == 1, nWords == 1, offset == 0
+ private static FDBigInteger genMillion1() {
+ return FDBigInteger.valueOfPow52(6, 0).leftShift(6);
+ }
+
+ // data.length == 2, nWords == 1, offset == 0
+ private static FDBigInteger genMillion2() {
+ return FDBigInteger.valueOfMulPow52(1000000L, 0, 0);
+ }
+
+ // data.length == 2, nWords == 2, offset == 0
+ private static FDBigInteger genTen18() {
+ return FDBigInteger.valueOfPow52(18, 0).leftShift(18);
+ }
+
+ private static void check(BigInteger expected, FDBigInteger actual, String message) throws Exception {
+ if (!expected.equals(toBigInteger(actual))) {
+ throw new Exception(message + " result " + actual + " expected " + expected.toString(16));
+ }
+ }
+
+ private static void testValueOfPow52(int p5, int p2) throws Exception {
+ check(biPow52(p5, p2), FDBigInteger.valueOfPow52(p5, p2),
+ "valueOfPow52(" + p5 + "," + p2 + ")");
+ }
+
+ private static void testValueOfPow52() throws Exception {
+ for (int p5 = 0; p5 <= MAX_P5; p5++) {
+ for (int p2 = 0; p2 <= MAX_P2; p2++) {
+ testValueOfPow52(p5, p2);
+ }
+ }
+ }
+
+ private static void testValueOfMulPow52(long value, int p5, int p2) throws Exception {
+ BigInteger bi = BigInteger.valueOf(value & ~LONG_SIGN_MASK);
+ if (value < 0) {
+ bi = bi.setBit(63);
+ }
+ check(biPow52(p5, p2).multiply(bi), FDBigInteger.valueOfMulPow52(value, p5, p2),
+ "valueOfMulPow52(" + Long.toHexString(value) + "." + p5 + "," + p2 + ")");
+ }
+
+ private static void testValueOfMulPow52(long value, int p5) throws Exception {
+ testValueOfMulPow52(value, p5, 0);
+ testValueOfMulPow52(value, p5, 1);
+ testValueOfMulPow52(value, p5, 30);
+ testValueOfMulPow52(value, p5, 31);
+ testValueOfMulPow52(value, p5, 33);
+ testValueOfMulPow52(value, p5, 63);
+ }
+
+ private static void testValueOfMulPow52() throws Exception {
+ for (int p5 = 0; p5 <= MAX_P5; p5++) {
+ testValueOfMulPow52(0xFFFFFFFFL, p5);
+ testValueOfMulPow52(0x123456789AL, p5);
+ testValueOfMulPow52(0x7FFFFFFFFFFFFFFFL, p5);
+ testValueOfMulPow52(0xFFFFFFFFFFF54321L, p5);
+ }
+ }
+
+ private static void testLeftShift(FDBigInteger t, int shift, boolean isImmutable) throws Exception {
+ BigInteger bt = toBigInteger(t);
+ FDBigInteger r = t.leftShift(shift);
+ if ((bt.signum() == 0 || shift == 0 || !isImmutable) && r != t) {
+ throw new Exception("leftShift doesn't reuse its argument");
+ }
+ if (isImmutable) {
+ check(bt, t, "leftShift corrupts its argument");
+ }
+ check(bt.shiftLeft(shift), r, "leftShift returns wrong result");
+ }
+
+ private static void testLeftShift() throws Exception {
+ testLeftShift(IMMUTABLE_ZERO, 0, true);
+ testLeftShift(IMMUTABLE_ZERO, 10, true);
+ testLeftShift(MUTABLE_ZERO, 0, false);
+ testLeftShift(MUTABLE_ZERO, 10, false);
+
+ testLeftShift(IMMUTABLE_MILLION, 0, true);
+ testLeftShift(IMMUTABLE_MILLION, 1, true);
+ testLeftShift(IMMUTABLE_MILLION, 12, true);
+ testLeftShift(IMMUTABLE_MILLION, 13, true);
+ testLeftShift(IMMUTABLE_MILLION, 32, true);
+ testLeftShift(IMMUTABLE_MILLION, 33, true);
+ testLeftShift(IMMUTABLE_MILLION, 44, true);
+ testLeftShift(IMMUTABLE_MILLION, 45, true);
+
+ testLeftShift(genMillion1(), 0, false);
+ testLeftShift(genMillion1(), 1, false);
+ testLeftShift(genMillion1(), 12, false);
+ testLeftShift(genMillion1(), 13, false);
+ testLeftShift(genMillion1(), 25, false);
+ testLeftShift(genMillion1(), 26, false);
+ testLeftShift(genMillion1(), 32, false);
+ testLeftShift(genMillion1(), 33, false);
+ testLeftShift(genMillion1(), 44, false);
+ testLeftShift(genMillion1(), 45, false);
+
+ testLeftShift(genMillion2(), 0, false);
+ testLeftShift(genMillion2(), 1, false);
+ testLeftShift(genMillion2(), 12, false);
+ testLeftShift(genMillion2(), 13, false);
+ testLeftShift(genMillion2(), 25, false);
+ testLeftShift(genMillion2(), 26, false);
+ testLeftShift(genMillion2(), 32, false);
+ testLeftShift(genMillion2(), 33, false);
+ testLeftShift(genMillion2(), 44, false);
+ testLeftShift(genMillion2(), 45, false);
+ }
+
+ private static void testQuoRemIteration(FDBigInteger t, FDBigInteger s) throws Exception {
+ BigInteger bt = toBigInteger(t);
+ BigInteger bs = toBigInteger(s);
+ int q = t.quoRemIteration(s);
+ BigInteger[] qr = bt.divideAndRemainder(bs);
+ if (!BigInteger.valueOf(q).equals(qr[0])) {
+ throw new Exception("quoRemIteration returns incorrect quo");
+ }
+ check(qr[1].multiply(BigInteger.TEN), t, "quoRemIteration returns incorrect rem");
+ }
+
+ private static void testQuoRemIteration() throws Exception {
+ // IMMUTABLE_TEN18 == 0de0b6b3a7640000
+ // q = 0
+ testQuoRemIteration(mutable("00000001", 0), IMMUTABLE_TEN18);
+ testQuoRemIteration(mutable("00000001", 1), IMMUTABLE_TEN18);
+ testQuoRemIteration(mutable("0de0b6b2", 1), IMMUTABLE_TEN18);
+ // q = 1 -> q = 0
+ testQuoRemIteration(mutable("0de0b6b3", 1), IMMUTABLE_TEN18);
+ testQuoRemIteration(mutable("0de0b6b3a763FFFF", 0), IMMUTABLE_TEN18);
+ // q = 1
+ testQuoRemIteration(mutable("0de0b6b3a7640000", 0), IMMUTABLE_TEN18);
+ testQuoRemIteration(mutable("0de0b6b3FFFFFFFF", 0), IMMUTABLE_TEN18);
+ testQuoRemIteration(mutable("8ac72304", 1), IMMUTABLE_TEN18);
+ testQuoRemIteration(mutable("0de0b6b400000000", 0), IMMUTABLE_TEN18);
+ testQuoRemIteration(mutable("8ac72305", 1), IMMUTABLE_TEN18);
+ // q = 18
+ testQuoRemIteration(mutable("FFFFFFFF", 1), IMMUTABLE_TEN18);
+ }
+
+ private static void testCmp(FDBigInteger t, FDBigInteger o) throws Exception {
+ BigInteger bt = toBigInteger(t);
+ BigInteger bo = toBigInteger(o);
+ int cmp = t.cmp(o);
+ int bcmp = bt.compareTo(bo);
+ if (bcmp != cmp) {
+ throw new Exception("cmp returns " + cmp + " expected " + bcmp);
+ }
+ check(bt, t, "cmp corrupts this");
+ check(bo, o, "cmp corrupts other");
+ if (o.cmp(t) != -cmp) {
+ throw new Exception("asymmetrical cmp");
+ }
+ check(bt, t, "cmp corrupts this");
+ check(bo, o, "cmp corrupts other");
+ }
+
+ private static void testCmp() throws Exception {
+ testCmp(mutable("FFFFFFFF", 0), mutable("100000000", 0));
+ testCmp(mutable("FFFFFFFF", 0), mutable("1", 1));
+ testCmp(mutable("5", 0), mutable("6", 0));
+ testCmp(mutable("5", 0), mutable("5", 0));
+ testCmp(mutable("5000000001", 0), mutable("500000001", 0));
+ testCmp(mutable("5000000001", 0), mutable("6", 1));
+ testCmp(mutable("5000000001", 0), mutable("5", 1));
+ testCmp(mutable("5000000000", 0), mutable("5", 1));
+ }
+
+ private static void testCmpPow52(FDBigInteger t, int p5, int p2) throws Exception {
+ FDBigInteger o = FDBigInteger.valueOfPow52(p5, p2);
+ BigInteger bt = toBigInteger(t);
+ BigInteger bo = biPow52(p5, p2);
+ int cmp = t.cmp(o);
+ int bcmp = bt.compareTo(bo);
+ if (bcmp != cmp) {
+ throw new Exception("cmp returns " + cmp + " expected " + bcmp);
+ }
+ check(bt, t, "cmp corrupts this");
+ check(bo, o, "cmpPow5 corrupts other");
+ }
+
+ private static void testCmpPow52() throws Exception {
+ testCmpPow52(mutable("00000002", 1), 0, 31);
+ testCmpPow52(mutable("00000002", 1), 0, 32);
+ testCmpPow52(mutable("00000002", 1), 0, 33);
+ testCmpPow52(mutable("00000002", 1), 0, 34);
+ testCmpPow52(mutable("00000002", 1), 0, 64);
+ testCmpPow52(mutable("00000003", 1), 0, 32);
+ testCmpPow52(mutable("00000003", 1), 0, 33);
+ testCmpPow52(mutable("00000003", 1), 0, 34);
+ }
+
+ private static void testAddAndCmp(FDBigInteger t, FDBigInteger x, FDBigInteger y) throws Exception {
+ BigInteger bt = toBigInteger(t);
+ BigInteger bx = toBigInteger(x);
+ BigInteger by = toBigInteger(y);
+ int cmp = t.addAndCmp(x, y);
+ int bcmp = bt.compareTo(bx.add(by));
+ if (bcmp != cmp) {
+ throw new Exception("addAndCmp returns " + cmp + " expected " + bcmp);
+ }
+ check(bt, t, "addAndCmp corrupts this");
+ check(bx, x, "addAndCmp corrupts x");
+ check(by, y, "addAndCmp corrupts y");
+ }
+
+ private static void testAddAndCmp() throws Exception {
+ testAddAndCmp(MUTABLE_ZERO, MUTABLE_ZERO, MUTABLE_ZERO);
+ testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, MUTABLE_ZERO);
+ testAddAndCmp(mutable("00000001", 0), mutable("00000001", 0), MUTABLE_ZERO);
+ testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000001", 0));
+ testAddAndCmp(mutable("00000001", 0), mutable("00000002", 0), MUTABLE_ZERO);
+ testAddAndCmp(mutable("00000001", 0), MUTABLE_ZERO, mutable("00000002", 0));
+ testAddAndCmp(mutable("00000001", 2), mutable("FFFFFFFF", 0), mutable("FFFFFFFF", 0));
+ testAddAndCmp(mutable("00000001", 0), mutable("00000001", 1), mutable("00000001", 0));
+
+ testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0F80000000", 1), mutable("F0F0F0F080000000", 1));
+ testAddAndCmp(mutable("00000001", 2), mutable("0F0F0F0E80000000", 1), mutable("F0F0F0F080000000", 1));
+
+ testAddAndCmp(mutable("00000002", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
+ testAddAndCmp(mutable("00000003", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
+ testAddAndCmp(mutable("00000004", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
+ testAddAndCmp(mutable("00000005", 1), mutable("0000000180000000", 1), mutable("0000000280000000", 1));
+
+ testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
+ testAddAndCmp(mutable("00000001", 2), mutable("8000000000000000", 0), mutable("8000000000000001", 0));
+ testAddAndCmp(mutable("00000002", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
+ testAddAndCmp(mutable("00000003", 2), mutable("8000000000000000", 0), mutable("8000000000000000", 0));
+ }
+
+ private static void testMultBy10(FDBigInteger t, boolean isImmutable) throws Exception {
+ BigInteger bt = toBigInteger(t);
+ FDBigInteger r = t.multBy10();
+ if ((bt.signum() == 0 || !isImmutable) && r != t) {
+ throw new Exception("multBy10 of doesn't reuse its argument");
+ }
+ if (isImmutable) {
+ check(bt, t, "multBy10 corrupts its argument");
+ }
+ check(bt.multiply(BigInteger.TEN), r, "multBy10 returns wrong result");
+ }
+
+ private static void testMultBy10() throws Exception {
+ for (int p5 = 0; p5 <= MAX_P5; p5++) {
+ for (int p2 = 0; p2 <= MAX_P2; p2++) {
+ // This strange way of creating a value ensures that it is mutable.
+ FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
+ testMultBy10(value, false);
+ value.makeImmutable();
+ testMultBy10(value, true);
+ }
+ }
+ }
+
+ private static void testMultByPow52(FDBigInteger t, int p5, int p2) throws Exception {
+ BigInteger bt = toBigInteger(t);
+ FDBigInteger r = t.multByPow52(p5, p2);
+ if (bt.signum() == 0 && r != t) {
+ throw new Exception("multByPow52 of doesn't reuse its argument");
+ }
+ check(bt.multiply(biPow52(p5, p2)), r, "multByPow52 returns wrong result");
+ }
+
+ private static void testMultByPow52() throws Exception {
+ for (int p5 = 0; p5 <= MAX_P5; p5++) {
+ for (int p2 = 0; p2 <= MAX_P2; p2++) {
+ // This strange way of creating a value ensures that it is mutable.
+ FDBigInteger value = FDBigInteger.valueOfPow52(0, 0).multByPow52(p5, p2);
+ testMultByPow52(value, p5, p2);
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ testValueOfPow52();
+ testValueOfMulPow52();
+ testLeftShift();
+ testQuoRemIteration();
+ testCmp();
+ testCmpPow52();
+ testAddAndCmp();
+ // Uncomment the following for more comprehensize but slow testing.
+ // testMultBy10();
+ // testMultByPow52();
+ }
+}
diff --git a/test/micro/org/openjdk/bench/java/lang/FloatingPointParse.java b/test/micro/org/openjdk/bench/java/lang/FloatingPointParse.java
new file mode 100644
index 00000000000..735f2760d3e
--- /dev/null
+++ b/test/micro/org/openjdk/bench/java/lang/FloatingPointParse.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2025, 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.
+ *
+ * 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 org.openjdk.bench.java.lang;
+
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OperationsPerInvocation;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Warmup;
+import org.openjdk.jmh.infra.Blackhole;
+
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@State(Scope.Thread)
+@Warmup(iterations = 3, time = 3)
+@Measurement(iterations = 3, time = 3)
+@Fork(value = 3)
+public class FloatingPointParse {
+
+ private static final int N = 1_000_000;
+ private static final int M = 100_000;
+
+ private String[] doubleToString, floatToString, s1, s2, s4, s10;
+
+ @Setup
+ public void setup() {
+ Random r = new Random();
+
+ doubleToString = new String[N];
+ for (int i = 0; i < doubleToString.length;) {
+ double v = Double.longBitsToDouble(r.nextLong());
+ if (Double.isFinite(v)) {
+ doubleToString[i++] = Double.toString(v);
+ }
+ }
+
+ floatToString = new String[N];
+ for (int i = 0; i < floatToString.length;) {
+ float v = Float.intBitsToFloat(r.nextInt());
+ if (Float.isFinite(v)) {
+ floatToString[i++] = Float.toString(v);
+ }
+ }
+
+ s1 = new String[M];
+ for (int i = 0; i < s1.length; ++i) {
+ String f = "0." + (r.nextLong() & 0x7fff_ffff_ffff_ffffL);
+ s1[i] = f + "e" + (r.nextInt(600) - 300);
+ }
+
+ s2 = new String[M];
+ for (int i = 0; i < s2.length; ++i) {
+ String f = "0." + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
+ + (r.nextLong() & 0x7fff_ffff_ffff_ffffL);
+ s2[i] = f + "e" + (r.nextInt(600) - 300);
+ }
+
+ s4 = new String[M];
+ for (int i = 0; i < s4.length; ++i) {
+ String f = "0." + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
+ + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
+ + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
+ + (r.nextLong() & 0x7fff_ffff_ffff_ffffL);
+ s4[i] = f + "e" + (r.nextInt(600) - 300);
+ }
+
+ s10 = new String[M];
+ for (int i = 0; i < s10.length; ++i) {
+ String f = "0." + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
+ + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
+ + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
+ + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
+ + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
+ + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
+ + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
+ + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
+ + (r.nextLong() & 0x7fff_ffff_ffff_ffffL)
+ + (r.nextLong() & 0x7fff_ffff_ffff_ffffL);
+ s10[i] = f + "e" + (r.nextInt(600) - 300);
+ }
+
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(N)
+ public void parseDoubleToString(Blackhole bh) {
+ for (String s : doubleToString) {
+ bh.consume(Double.parseDouble(s));
+ }
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(M)
+ public void parseDoubleS1(Blackhole bh) {
+ for (String s : s1) {
+ bh.consume(Double.parseDouble(s));
+ }
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(M)
+ public void parseDoubleS2(Blackhole bh) {
+ for (String s : s2) {
+ bh.consume(Double.parseDouble(s));
+ }
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(M)
+ public void parseDoubleS4(Blackhole bh) {
+ for (String s : s4) {
+ bh.consume(Double.parseDouble(s));
+ }
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(M)
+ public void parseDoubleS10(Blackhole bh) {
+ for (String s : s10) {
+ bh.consume(Double.parseDouble(s));
+ }
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(N)
+ public void parseFloatToString(Blackhole bh) {
+ for (String s : floatToString) {
+ bh.consume(Float.parseFloat(s));
+ }
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(M)
+ public void parseFloatS1(Blackhole bh) {
+ for (String s : s1) {
+ bh.consume(Float.parseFloat(s));
+ }
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(M)
+ public void parseFloatS2(Blackhole bh) {
+ for (String s : s2) {
+ bh.consume(Float.parseFloat(s));
+ }
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(M)
+ public void parseFloatS4(Blackhole bh) {
+ for (String s : s4) {
+ bh.consume(Float.parseFloat(s));
+ }
+ }
+
+ @Benchmark
+ @OperationsPerInvocation(M)
+ public void parseFloatS10(Blackhole bh) {
+ for (String s : s10) {
+ bh.consume(Float.parseFloat(s));
+ }
+ }
+
+}