undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2005, 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.
*/
/*
* @test
* @bug 5037596
* @summary Verify bitwise conversion works for non-canonical NaN values
* @library ../Math
* @build DoubleConsts
* @run main BitwiseConversion
*/
import static java.lang.Double.*;
public class BitwiseConversion {
static int testNanCase(long x) {
int errors = 0;
// Strip out sign and exponent bits
long y = x & DoubleConsts.SIGNIF_BIT_MASK;
double values[] = {
longBitsToDouble(DoubleConsts.EXP_BIT_MASK | y),
longBitsToDouble(DoubleConsts.SIGN_BIT_MASK | DoubleConsts.EXP_BIT_MASK | y)
};
for(double value: values) {
if (!isNaN(value)) {
throw new RuntimeException("Invalid input " + y +
"yielded non-NaN" + value);
}
long converted = doubleToLongBits(value);
if (converted != 0x7ff8000000000000L) {
errors++;
System.err.format("Non-canoncial NaN bits returned: %x%n",
converted);
}
}
return errors;
}
public static void main(String... argv) {
int errors = 0;
for (int i = 0; i < DoubleConsts.SIGNIFICAND_WIDTH-1; i++) {
errors += testNanCase(1L<<i);
}
if (doubleToLongBits(Double.POSITIVE_INFINITY)
!= 0x7ff0000000000000L) {
errors++;
System.err.println("Bad conversion for +infinity.");
}
if (doubleToLongBits(Double.NEGATIVE_INFINITY)
!= 0xfff0000000000000L) {
errors++;
System.err.println("Bad conversion for -infinity.");
}
if (errors > 0)
throw new RuntimeException();
}
}

View file

@ -0,0 +1,68 @@
/*
* 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
* 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.
*/
/*
* @test
* @compile Constants.java
* @bug 4397405 4826652
* @summary Testing constant-ness of Double.{MIN_VALUE, MAX_VALUE}, etc.
*/
public class Constants {
/*
* This compile-only test is to make sure that the primitive
* public static final fields in java.lang.Double are "constant
* expressions" as defined by "The Java Language Specification,
* 2nd edition" section 15.28; a different test checks the values
* of those fields.
*/
public static void main(String[] args) throws Exception {
int i = 0;
switch (i) {
case (int)Double.NaN: // 0
System.out.println("Double.NaN is a constant!");
break;
case (int)Double.MIN_VALUE + 1: // 0 + 1
System.out.println("Double.MIN_VALUE is a constant!");
break;
case (int)Double.MIN_NORMAL + 2: // 0 + 2
System.out.println("Double.MIN_NORMAL is a constant!");
break;
case Double.MIN_EXPONENT: // -1022
System.out.println("Double.MIN_EXPONENT is a constant!");
break;
case Double.MAX_EXPONENT: // 1023
System.out.println("Double.MAX_EXPONENT is a constant!");
break;
case (int)Double.MAX_VALUE - 1: // Integer.MAX_VALUE - 1
System.out.println("Double.MAX_VALUE is a constant!");
break;
case (int)Double.POSITIVE_INFINITY: // Integer.MAX_VALUE
System.out.println("Double.POSITIVE_INFINITY is a constant!");
break;
case (int)Double.NEGATIVE_INFINITY: // Integer.MIN_VALUE
System.out.println("Double.NEGATIVE_INFINITY is a constant!");
break;
}
}
}

View file

@ -0,0 +1,44 @@
/*
* 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
* 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.
*/
/*
* @test
* @bug 4408489 4826652
* @summary Testing values of Double.{MIN_VALUE, MIN_NORMAL, MAX_VALUE}
*/
public class Extrema {
public static void main(String[] args) throws Exception {
if (Double.MIN_VALUE != Double.longBitsToDouble(0x1L))
throw new RuntimeException("Double.MIN_VALUE is not equal "+
"to longBitsToDouble(0x1L).");
if (Double.MIN_NORMAL != Double.longBitsToDouble(0x0010000000000000L))
throw new RuntimeException("Double.MIN_NORMAL is not equal "+
"to longBitsToDouble(0x0010000000000000L).");
if (Double.MAX_VALUE != Double.longBitsToDouble(0x7fefffffffffffffL))
throw new RuntimeException("Double.MAX_VALUE is not equal "+
"to longBitsToDouble(0x7fefffffffffffffL).");
}
}

View file

@ -0,0 +1,168 @@
/*
* 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
* 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.
*/
/*
* @test
* @bug 4428772
* @summary Testing recognition of "NaN" and "Infinity" strings
*/
public class NaNInfinityParsing {
/*
* Regression tests for:
* 4428772 -- Establish invariant for Float & Double classes and
* their string representations
*
* Added capability for parse{Float, Double} and related methods
* to recognize "NaN" and "Infinity" strings so that
* parseDouble(toString(d)) will always return the original
* floating-point value.
*/
static String NaNStrings[] = {
"NaN",
"+NaN",
"-NaN"
};
static String infinityStrings[] = {
"Infinity",
"+Infinity",
"-Infinity",
};
static String invalidStrings[] = {
"+",
"-",
"@",
"N",
"Na",
"Nan",
"NaNf",
"NaNd",
"NaNF",
"NaND",
"+N",
"+Na",
"+Nan",
"+NaNf",
"+NaNd",
"+NaNF",
"+NaND",
"-N",
"-Na",
"-Nan",
"-NaNf",
"-NaNd",
"-NaNF",
"-NaND",
"I",
"In",
"Inf",
"Infi",
"Infin",
"Infini",
"Infinit",
"InfinitY",
"Infinityf",
"InfinityF",
"Infinityd",
"InfinityD",
"+I",
"+In",
"+Inf",
"+Infi",
"+Infin",
"+Infini",
"+Infinit",
"+InfinitY",
"+Infinityf",
"+InfinityF",
"+Infinityd",
"+InfinityD",
"-I",
"-In",
"-Inf",
"-Infi",
"-Infin",
"-Infini",
"-Infinit",
"-InfinitY",
"-Infinityf",
"-InfinityF",
"-Infinityd",
"-InfinityD",
"NaNInfinity",
"InfinityNaN",
"nan",
"infinity"
};
public static void main(String [] argv) throws Exception {
int i;
double d;
// Test valid NaN strings
for(i = 0; i < NaNStrings.length; i++) {
if(!Double.isNaN(d=Double.parseDouble(NaNStrings[i]))) {
throw new RuntimeException("NaN string ``" + NaNStrings[i]
+ "'' did not parse as a NaN; returned " +
d + " instead.");
}
}
// Test valid Infinity strings
for(i = 0; i < infinityStrings.length; i++) {
if(!Double.isInfinite(d=Double.parseDouble(infinityStrings[i]))) {
throw new RuntimeException("Infinity string ``" +
infinityStrings[i] +
"'' did not parse as infinity; returned " +
d + "instead.");
}
// check sign of result
boolean negative = (infinityStrings[i].charAt(0) == '-');
if(d != (negative?Double.NEGATIVE_INFINITY:
Double.POSITIVE_INFINITY))
throw new RuntimeException("Infinity has wrong sign;" +
(negative?"positive instead of negative.":
"negative instead of positive."));
}
// Test almost valid strings
for(i = 0; i < invalidStrings.length; i++) {
try {
double result;
d = Double.parseDouble(invalidStrings[i]);
throw new RuntimeException("Invalid string ``" +
invalidStrings[i]
+"'' parsed as " + d + ".");
}
catch(NumberFormatException e) {
// expected
}
}
}
}

View file

@ -0,0 +1,816 @@
/*
* 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
* 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.
*/
/*
* @test
* @bug 4160406 4705734 4707389 4826774 4895911 4421494 6358355 7021568
* 7039369 4396272 8366017
* @summary Test for Double.parseDouble method and acceptance regex
*/
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.regex.*;
public class ParseDouble {
private static final BigDecimal HALF = BigDecimal.valueOf(0.5);
private static void fail(String val, double n) {
throw new RuntimeException("Double.parseDouble failed. String:" +
val + " Result:" + n);
}
private static void check(String val) {
double n = Double.parseDouble(val);
boolean isNegativeN = n < 0 || n == 0 && 1/n < 0;
double na = Math.abs(n);
String s = val.trim().toLowerCase();
switch (s.charAt(s.length() - 1)) {
case 'd':
case 'f':
s = s.substring(0, s.length() - 1);
break;
}
boolean isNegative = false;
if (s.charAt(0) == '+') {
s = s.substring(1);
} else if (s.charAt(0) == '-') {
s = s.substring(1);
isNegative = true;
}
if (s.equals("nan")) {
if (!Double.isNaN(n)) {
fail(val, n);
}
return;
}
if (Double.isNaN(n)) {
fail(val, n);
}
if (isNegativeN != isNegative)
fail(val, n);
if (s.equals("infinity")) {
if (na != Double.POSITIVE_INFINITY) {
fail(val, n);
}
return;
}
BigDecimal bd;
if (s.startsWith("0x")) {
s = s.substring(2);
int indP = s.indexOf('p');
long exp = Long.parseLong(s.substring(indP + 1));
int indD = s.indexOf('.');
String significand;
if (indD >= 0) {
significand = s.substring(0, indD) + s.substring(indD + 1, indP);
exp -= 4*(indP - indD - 1);
} else {
significand = s.substring(0, indP);
}
bd = new BigDecimal(new BigInteger(significand, 16));
if (exp >= 0) {
bd = bd.multiply(BigDecimal.valueOf(2).pow((int)exp));
} else {
bd = bd.divide(BigDecimal.valueOf(2).pow((int)-exp));
}
} else {
bd = new BigDecimal(s);
}
BigDecimal l, u;
if (Double.isInfinite(na)) {
l = new BigDecimal(Double.MAX_VALUE).add(new BigDecimal(Math.ulp(Double.MAX_VALUE)).multiply(HALF));
u = null;
} else {
l = new BigDecimal(na).subtract(new BigDecimal(Math.ulp(Math.nextUp(-na))).multiply(HALF));
u = new BigDecimal(na).add(new BigDecimal(Math.ulp(n)).multiply(HALF));
}
int cmpL = bd.compareTo(l);
int cmpU = u != null ? bd.compareTo(u) : -1;
if ((Double.doubleToLongBits(n) & 1) != 0) {
if (cmpL <= 0 || cmpU >= 0) {
fail(val, n);
}
} else {
if (cmpL < 0 || cmpU > 0) {
fail(val, n);
}
}
}
private static void check(String val, double expected) {
double n = Double.parseDouble(val);
if (n != expected)
fail(val, n);
check(val);
}
private static void rudimentaryTest() {
check(new String(""+Double.MIN_VALUE), Double.MIN_VALUE);
check(new String(""+Double.MAX_VALUE), Double.MAX_VALUE);
check("10", (double) 10.0);
check("10.0", (double) 10.0);
check("10.01", (double) 10.01);
check("-10", (double) -10.0);
check("-10.00", (double) -10.0);
check("-10.01", (double) -10.01);
}
static String badStrings[] = {
"",
"+",
"-",
"+e",
"-e",
"+e170",
"-e170",
// Make sure intermediate white space is not deleted.
"1234 e10",
"-1234 e10",
// Control characters in the interior of a string are not legal
"1\u0007e1",
"1e\u00071",
// NaN and infinity can't have trailing type suffices or exponents
"NaNf",
"NaNF",
"NaNd",
"NaND",
"-NaNf",
"-NaNF",
"-NaNd",
"-NaND",
"+NaNf",
"+NaNF",
"+NaNd",
"+NaND",
"Infinityf",
"InfinityF",
"Infinityd",
"InfinityD",
"-Infinityf",
"-InfinityF",
"-Infinityd",
"-InfinityD",
"+Infinityf",
"+InfinityF",
"+Infinityd",
"+InfinityD",
"NaNe10",
"-NaNe10",
"+NaNe10",
"Infinitye10",
"-Infinitye10",
"+Infinitye10",
// Non-ASCII digits are not recognized
"\u0661e\u0661", // 1e1 in Arabic-Indic digits
"\u06F1e\u06F1", // 1e1 in Extended Arabic-Indic digits
"\u0967e\u0967", // 1e1 in Devanagari digits
"\uD835\uDFD9e\uD835\uDFD9", // 1e1 in Mathematical Alphanumeric Symbols
// JCK test lex03592m3
".",
// JCK test lex03592m4
"e42",
// JCK test lex03592m5
".e42",
// JCK test lex03592m6
"d",
// JCK test lex03592m7
".d",
// JCK test lex03592m8
"e42d",
// JCK test lex03592m9
".e42d",
// JCK test lex03593m10
"1A01.01125e-10d",
// JCK test lex03593m11
"2;3.01125e-10d",
// JCK test lex03593m12
"1_34.01125e-10d",
// JCK test lex03593m14
"202..01125e-10d",
// JCK test lex03593m15
"202,01125e-10d",
// JCK test lex03593m16
"202.03b4e-10d",
// JCK test lex03593m18
"202.06_3e-10d",
// JCK test lex03593m20
"202.01125e-f0d",
// JCK test lex03593m21
"202.01125e_3d",
// JCK test lex03593m22
"202.01125e -5d",
// JCK test lex03593m24
"202.01125e-10r",
// JCK test lex03593m25
"202.01125e-10ff",
// JCK test lex03593m26
"1234L.01",
// JCK test lex03593m27
"12ee-2",
// JCK test lex03593m28
"12e-2.2.2",
// JCK test lex03593m29
"12.01e+",
// JCK test lex03593m30
"12.01E",
// Bad hexadecimal-style strings
// Two leading zeros
"00x1.0p1",
// Must have hex specifier
"1.0p1",
"00010p1",
"deadbeefp1",
// Need an explicit fully-formed exponent
"0x1.0p",
"0x1.0",
// Exponent must be in decimal
"0x1.0pa",
"0x1.0pf",
// Exponent separated by "p"
"0x1.0e22",
"0x1.0e22",
// Need a signifcand
"0xp22"
};
static String goodStrings[] = {
"NaN",
"+NaN",
"-NaN",
"Infinity",
"+Infinity",
"-Infinity",
"1.1e-23f",
".1e-23f",
"1e-23",
"1f",
"0",
"-0",
"+0",
"00",
"00",
"-00",
"+00",
"0000000000",
"-0000000000",
"+0000000000",
"1",
"2",
"1234",
"-1234",
"+1234",
"2147483647", // Integer.MAX_VALUE
"2147483648",
"-2147483648", // Integer.MIN_VALUE
"-2147483649",
"16777215",
"16777216", // 2^24
"16777217",
"-16777215",
"-16777216", // -2^24
"-16777217",
"9007199254740991",
"9007199254740992", // 2^53
"9007199254740993",
"-9007199254740991",
"-9007199254740992", // -2^53
"-9007199254740993",
"9223372036854775807",
"9223372036854775808", // Long.MAX_VALUE
"9223372036854775809",
"-9223372036854775808",
"-9223372036854775809", // Long.MIN_VALUE
"-9223372036854775810",
// Culled from JCK test lex03591m1
"54.07140d",
"7.01e-324d",
"2147483647.01d",
"1.2147483647f",
"000000000000000000000000001.F",
"1.00000000000000000000000000e-2F",
// Culled from JCK test lex03592m2
"2.",
".0909",
"122112217090.0",
"7090e-5",
"2.E-20",
".0909e42",
"122112217090.0E+100",
"7090f",
"2.F",
".0909d",
"122112217090.0D",
"7090e-5f",
"2.E-20F",
".0909e42d",
"122112217090.0E+100D",
// Culled from JCK test lex03594m31 -- unicode escapes
"\u0035\u0031\u0034\u0039\u0032\u0033\u0036\u0037\u0038\u0030.1102E-209D",
"1290873\u002E12301e100",
"1.1E-10\u0066",
// Culled from JCK test lex03595m1
"0.0E-10",
"1E10",
// Culled from JCK test lex03691m1
"0.f",
"1f",
"0.F",
"1F",
"0.12d",
"1e-0d",
"12.e+1D",
"0e-0D",
"12.e+01",
"1e-01",
// Good hex strings
// Vary capitalization of separators.
"0x1p1",
"0X1p1",
"0x1P1",
"0X1P1",
"0x1p1f",
"0X1p1f",
"0x1P1f",
"0X1P1f",
"0x1p1F",
"0X1p1F",
"0x1P1F",
"0X1P1F",
"0x1p1d",
"0X1p1d",
"0x1P1d",
"0X1P1d",
"0x1p1D",
"0X1p1D",
"0x1P1D",
"0X1P1D",
"-0x1p1",
"-0X1p1",
"-0x1P1",
"-0X1P1",
"-0x1p1f",
"-0X1p1f",
"-0x1P1f",
"-0X1P1f",
"-0x1p1F",
"-0X1p1F",
"-0x1P1F",
"-0X1P1F",
"-0x1p1d",
"-0X1p1d",
"-0x1P1d",
"-0X1P1d",
"-0x1p1D",
"-0X1p1D",
"-0x1P1D",
"-0X1P1D",
"0x1p-1",
"0X1p-1",
"0x1P-1",
"0X1P-1",
"0x1p-1f",
"0X1p-1f",
"0x1P-1f",
"0X1P-1f",
"0x1p-1F",
"0X1p-1F",
"0x1P-1F",
"0X1P-1F",
"0x1p-1d",
"0X1p-1d",
"0x1P-1d",
"0X1P-1d",
"0x1p-1D",
"0X1p-1D",
"0x1P-1D",
"0X1P-1D",
"-0x1p-1",
"-0X1p-1",
"-0x1P-1",
"-0X1P-1",
"-0x1p-1f",
"-0X1p-1f",
"-0x1P-1f",
"-0X1P-1f",
"-0x1p-1F",
"-0X1p-1F",
"-0x1P-1F",
"-0X1P-1F",
"-0x1p-1d",
"-0X1p-1d",
"-0x1P-1d",
"-0X1P-1d",
"-0x1p-1D",
"-0X1p-1D",
"-0x1P-1D",
"-0X1P-1D",
// Try different significand combinations
"0xap1",
"0xbp1",
"0xcp1",
"0xdp1",
"0xep1",
"0xfp1",
"0x1p1",
"0x.1p1",
"0x1.1p1",
"0x001p23",
"0x00.1p1",
"0x001.1p1",
"0x100p1",
"0x.100p1",
"0x1.100p1",
"0x00100p1",
"0x00.100p1",
"0x001.100p1",
// Limits
"1.7976931348623157E308", // Double.MAX_VALUE
"4.9e-324", // Double.MIN_VALUE
"2.2250738585072014e-308", // Double.MIN_NORMAL
"2.2250738585072012e-308", // near Double.MIN_NORMAL
"1.7976931348623158e+308", // near MAX_VALUE + ulp(MAX_VALUE)/2
"1.7976931348623159e+308", // near MAX_VALUE + ulp(MAX_VALUE)
"2.4703282292062329e-324", // above MIN_VALUE/2
"2.4703282292062327e-324", // MIN_VALUE/2
"2.4703282292062325e-324", // below MIN_VALUE/2
// 1e308 with leading zeros
"0.0000000000001e321",
"00.000000000000000001e326",
"00000.000000000000000001e326",
"000.0000000000000000001e327",
"0.00000000000000000001e328",
};
static String paddedBadStrings[];
static String paddedGoodStrings[];
static {
String pad = " \t\n\r\f\u0001\u000b\u001f";
paddedBadStrings = new String[badStrings.length];
for(int i = 0 ; i < badStrings.length; i++)
paddedBadStrings[i] = pad + badStrings[i] + pad;
paddedGoodStrings = new String[goodStrings.length];
for(int i = 0 ; i < goodStrings.length; i++)
paddedGoodStrings[i] = pad + goodStrings[i] + pad;
}
/*
* Throws an exception if <code>Input</code> is
* <code>exceptionalInput</code> and {@link Double.parseDouble
* parseDouble} does <em>not</em> throw an exception or if
* <code>Input</code> is not <code>exceptionalInput</code> and
* <code>parseDouble</code> throws an exception. This method does
* not attempt to test whether the string is converted to the
* proper value; just whether the input is accepted appropriately
* or not.
*/
private static void testParsing(String [] input,
boolean exceptionalInput) {
for (String s : input) {
try {
Double.parseDouble(s);
check(s);
} catch (NumberFormatException e) {
if (!exceptionalInput) {
throw new RuntimeException("Double.parseDouble rejected " +
"good string `" + s +
"'.");
}
continue;
}
if (exceptionalInput) {
throw new RuntimeException("Double.parseDouble accepted " +
"bad string `" + s +
"'.");
}
}
}
/*
* Throws an exception if <code>Input</code> is
* <code>exceptionalInput</code> and the regular expression
* matches one of the strings or if <code>Input</code> is not
* <code>exceptionalInput</code> and the regular expression fails
* to match an input string.
*/
private static void testRegex(String [] input, boolean exceptionalInput) {
/*
* The regex below is taken from the JavaDoc for
* Double.valueOf.
*/
final String Digits = "(\\p{Digit}+)";
final String HexDigits = "(\\p{XDigit}+)";
// an exponent is 'e' or 'E' followed by an optionally
// signed decimal integer.
final String Exp = "[eE][+-]?"+Digits;
final String fpRegex =
("[\\x00-\\x20]*"+ // Optional leading "whitespace"
"[+-]?(" + // Optional sign character
"NaN|" + // "NaN" string
"Infinity|" + // "Infinity" string
// A floating-point string representing a finite positive
// number without a leading sign has at most five basic pieces:
// Digits . Digits ExponentPart FloatTypeSuffix
//
// Since this method allows integer-only strings as input
// in addition to strings of floating-point literals, the
// two sub-patterns below are simplifications of the grammar
// productions from the Java Language Specification, 2nd
// edition, section 3.10.2.
// A decimal floating-point string representing a finite positive
// number without a leading sign has at most five basic pieces:
// Digits . Digits ExponentPart FloatTypeSuffix
//
// Since this method allows integer-only strings as input
// in addition to strings of floating-point literals, the
// two sub-patterns below are simplifications of the grammar
// productions from the Java Language Specification, 2nd
// edition, section 3.10.2.
// Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt
"(((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+
// . Digits ExponentPart_opt FloatTypeSuffix_opt
"(\\.("+Digits+")("+Exp+")?))|"+
// Hexadecimal strings
"((" +
// 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt
"(0[xX]" + HexDigits + "(\\.)?)|" +
// 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt
"(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" +
")[pP][+-]?" + Digits + "))" +
"[fFdD]?))" +
"[\\x00-\\x20]*");// Optional trailing "whitespace"
Pattern fpPattern = Pattern.compile(fpRegex);
for(int i = 0; i < input.length; i++) {
Matcher m = fpPattern.matcher(input[i]);
if (m.matches() != ! exceptionalInput) {
throw new RuntimeException("Regular expression " +
(exceptionalInput?
"accepted bad":
"rejected good") +
" string `" +
input[i] + "'.");
}
}
}
/**
* For each subnormal power of two, test at boundaries of
* region that should convert to that value.
*/
private static void testSubnormalPowers() {
boolean failed = false;
BigDecimal TWO = BigDecimal.valueOf(2);
// An ulp is the same for all subnormal values
BigDecimal ulp_BD = new BigDecimal(Double.MIN_VALUE);
// Test subnormal powers of two (except Double.MIN_VALUE)
for(int i = -1073; i <= -1022; i++) {
double d = Math.scalb(1.0, i);
/*
* The region [d - ulp/2, d + ulp/2] should round to d.
*/
BigDecimal d_BD = new BigDecimal(d);
BigDecimal lowerBound = d_BD.subtract(ulp_BD.divide(TWO));
BigDecimal upperBound = d_BD.add(ulp_BD.divide(TWO));
double convertedLowerBound = Double.parseDouble(lowerBound.toString());
double convertedUpperBound = Double.parseDouble(upperBound.toString());
if (convertedLowerBound != d) {
failed = true;
System.out.printf("2^%d lowerBound converts as %a %s%n",
i, convertedLowerBound, lowerBound);
}
if (convertedUpperBound != d) {
failed = true;
System.out.printf("2^%d upperBound converts as %a %s%n",
i, convertedUpperBound, upperBound);
}
}
/*
* Double.MIN_VALUE
* The region ]0.5*Double.MIN_VALUE, 1.5*Double.MIN_VALUE[ should round to Double.MIN_VALUE .
*/
BigDecimal minValue = new BigDecimal(Double.MIN_VALUE);
if (Double.parseDouble(minValue.multiply(new BigDecimal(0.5)).toString()) != 0.0) {
failed = true;
System.out.printf("0.5*MIN_VALUE doesn't convert 0%n");
}
if (Double.parseDouble(minValue.multiply(new BigDecimal(0.50000000001)).toString()) != Double.MIN_VALUE) {
failed = true;
System.out.printf("0.50000000001*MIN_VALUE doesn't convert to MIN_VALUE%n");
}
if (Double.parseDouble(minValue.multiply(new BigDecimal(1.49999999999)).toString()) != Double.MIN_VALUE) {
failed = true;
System.out.printf("1.49999999999*MIN_VALUE doesn't convert to MIN_VALUE%n");
}
if (Double.parseDouble(minValue.multiply(new BigDecimal(1.5)).toString()) != 2*Double.MIN_VALUE) {
failed = true;
System.out.printf("1.5*MIN_VALUE doesn't convert to 2*MIN_VALUE%n");
}
if (failed)
throw new RuntimeException("Inconsistent conversion");
}
/**
* For each power of two, test at boundaries of
* region that should convert to that value.
*/
private static void testPowers() {
for(int i = -1074; i <= +1023; i++) {
double d = Math.scalb(1.0, i);
BigDecimal d_BD = new BigDecimal(d);
BigDecimal lowerBound = d_BD.subtract(new BigDecimal(Math.ulp(Math.nextUp(-d))).multiply(HALF));
BigDecimal upperBound = d_BD.add(new BigDecimal(Math.ulp(d)).multiply(HALF));
check(lowerBound.toString());
check(upperBound.toString());
}
check(new BigDecimal(Double.MAX_VALUE).add(new BigDecimal(Math.ulp(Double.MAX_VALUE)).multiply(HALF)).toString());
}
private static void testStrictness() {
final double expected = 0x0.0000008000000p-1022;
// final double expected = 0x0.0000008000001p-1022;
boolean failed = false;
double conversion = 0.0;
double sum = 0.0; // Prevent conversion from being optimized away
//2^-1047 + 2^-1075 rounds to 2^-1047
String decimal = "6.631236871469758276785396630275967243399099947355303144249971758736286630139265439618068200788048744105960420552601852889715006376325666595539603330361800519107591783233358492337208057849499360899425128640718856616503093444922854759159988160304439909868291973931426625698663157749836252274523485312442358651207051292453083278116143932569727918709786004497872322193856150225415211997283078496319412124640111777216148110752815101775295719811974338451936095907419622417538473679495148632480391435931767981122396703443803335529756003353209830071832230689201383015598792184172909927924176339315507402234836120730914783168400715462440053817592702766213559042115986763819482654128770595766806872783349146967171293949598850675682115696218943412532098591327667236328125E-316";
for(int i = 0; i <= 12_000; i++) {
conversion = Double.parseDouble(decimal);
sum += conversion;
if (conversion != expected) {
failed = true;
System.out.printf("Iteration %d converts as %a%n",
i, conversion);
}
}
System.out.println("Sum = " + sum);
if (failed)
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();
testParsing(goodStrings, false);
testParsing(paddedGoodStrings, false);
testParsing(badStrings, true);
testParsing(paddedBadStrings, true);
testRegex(goodStrings, false);
testRegex(paddedGoodStrings, false);
testRegex(badStrings, true);
testRegex(paddedBadStrings, true);
testSubnormalPowers();
testPowers();
testStrictness();
testFastPaths();
}
}

View file

@ -0,0 +1,448 @@
/*
* Copyright (c) 2003, 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.
*/
/*
* @test
* @library /test/lib
* @build jdk.test.lib.RandomFactory
* @run main ParseHexFloatingPoint
* @bug 4826774 8078672
* @summary Numerical tests for hexadecimal inputs to parse{Double, Float} (use -Dseed=X to set PRNG seed)
* @key randomness
*/
import jdk.test.lib.RandomFactory;
public class ParseHexFloatingPoint {
private ParseHexFloatingPoint(){}
public static final double infinityD = Double.POSITIVE_INFINITY;
public static final double NaND = Double.NaN;
static int test(String testName, String input,
double result, double expected) {
int failures =0;
if (Double.compare(result, expected) != 0 ) {
System.err.println("Failure for " + testName +
": For input " + input +
" expected " + expected +
" got " + result + ".");
}
return failures;
}
static int testCase(String input, double expected) {
int failures =0;
// Try different combination of letter components
input = input.toLowerCase(java.util.Locale.US);
String [] suffices = {"", "f", "F", "d", "D"};
String [] signs = {"", "-", "+"};
for(int i = 0; i < 2; i++) {
String s1 = input;
if(i == 1)
s1 = s1.replace('x', 'X');
for(int j = 0; j < 2; j++) {
String s2 = s1;
if(j == 1)
s2 = s2.replace('p', 'P');
for(int k = 0; k < 2; k++) {
String s3 = s2;
if(k == 1)
s3 = upperCaseHex(s3);
for(int m = 0; m < suffices.length; m++) {
String s4 = s3 + suffices[m];
for(int n = 0; n < signs.length; n++) {
String s5 = signs[n] + s4;
double result = Double.parseDouble(s5);
failures += test("Double.parseDouble",
s5, result, (signs[n].equals("-") ?
-expected:
expected));
}
}
}
}
}
return failures;
}
static String upperCaseHex(String s) {
return s.replace('a', 'A').replace('b', 'B').replace('c', 'C').
replace('d', 'D').replace('e','E').replace('f', 'F');
}
/*
* Test easy and tricky double rounding cases.
*/
static int doubleTests() {
/*
* A String, double pair
*/
class PairSD {
public String s;
public double d;
PairSD(String s, double d) {
this.s = s;
this.d = d;
}
}
int failures = 0;
// Hex strings that convert to three; test basic functionality
// of significand and exponent shift adjusts along with the
// no-op of adding leading zeros. These cases don't exercise
// the rounding code.
String leadingZeros = "0x0000000000000000000";
String [] threeTests = {
"0x.003p12",
"0x.006p11",
"0x.00cp10",
"0x.018p9",
"0x.3p4",
"0x.6p3",
"0x.cp2",
"0x1.8p1",
"0x3p0",
"0x6.0p-1",
"0xc.0p-2",
"0x18.0p-3",
"0x3000000p-24",
"0x3.0p0",
"0x3.000000p0",
};
for(int i=0; i < threeTests.length; i++) {
String input = threeTests[i];
failures += testCase(input, 3.0);
input.replaceFirst("^0x", leadingZeros);
failures += testCase(input, 3.0);
}
long bigExponents [] = {
2*Double.MAX_EXPONENT,
2*Double.MIN_EXPONENT,
(long)Integer.MAX_VALUE-1,
(long)Integer.MAX_VALUE,
(long)Integer.MAX_VALUE+1,
(long)Integer.MIN_VALUE-1,
(long)Integer.MIN_VALUE,
(long)Integer.MIN_VALUE+1,
Long.MAX_VALUE-1,
Long.MAX_VALUE,
Long.MIN_VALUE+1,
Long.MIN_VALUE,
};
// Test zero significand with large exponents.
for(int i = 0; i < bigExponents.length; i++) {
failures += testCase("0x0.0p"+Long.toString(bigExponents[i]) , 0.0);
}
// Test nonzero significand with large exponents.
for(int i = 0; i < bigExponents.length; i++) {
long exponent = bigExponents[i];
failures += testCase("0x10000.0p"+Long.toString(exponent) ,
(exponent <0?0.0:infinityD));
}
// Test significands with different lengths and bit patterns.
{
long signif = 0;
for(int i = 1; i <= 0xe; i++) {
signif = (signif <<4) | (long)i;
failures += testCase("0x"+Long.toHexString(signif)+"p0", signif);
}
}
PairSD [] testCases = {
new PairSD("0x0.0p0", 0.0/16.0),
new PairSD("0x0.1p0", 1.0/16.0),
new PairSD("0x0.2p0", 2.0/16.0),
new PairSD("0x0.3p0", 3.0/16.0),
new PairSD("0x0.4p0", 4.0/16.0),
new PairSD("0x0.5p0", 5.0/16.0),
new PairSD("0x0.6p0", 6.0/16.0),
new PairSD("0x0.7p0", 7.0/16.0),
new PairSD("0x0.8p0", 8.0/16.0),
new PairSD("0x0.9p0", 9.0/16.0),
new PairSD("0x0.ap0", 10.0/16.0),
new PairSD("0x0.bp0", 11.0/16.0),
new PairSD("0x0.cp0", 12.0/16.0),
new PairSD("0x0.dp0", 13.0/16.0),
new PairSD("0x0.ep0", 14.0/16.0),
new PairSD("0x0.fp0", 15.0/16.0),
// Half-way case between zero and MIN_VALUE rounds down to
// zero
new PairSD("0x1.0p-1075", 0.0),
// Slighly more than half-way case between zero and
// MIN_VALUES rounds up to zero.
new PairSD("0x1.1p-1075", Double.MIN_VALUE),
new PairSD("0x1.000000000001p-1075", Double.MIN_VALUE),
new PairSD("0x1.000000000000001p-1075", Double.MIN_VALUE),
// More subnormal rounding tests
new PairSD("0x0.fffffffffffff7fffffp-1022", Math.nextDown(Double.MIN_NORMAL)),
new PairSD("0x0.fffffffffffff8p-1022", Double.MIN_NORMAL),
new PairSD("0x0.fffffffffffff800000001p-1022",Double.MIN_NORMAL),
new PairSD("0x0.fffffffffffff80000000000000001p-1022",Double.MIN_NORMAL),
new PairSD("0x1.0p-1022", Double.MIN_NORMAL),
// Large value and overflow rounding tests
new PairSD("0x1.fffffffffffffp1023", Double.MAX_VALUE),
new PairSD("0x1.fffffffffffff0000000p1023", Double.MAX_VALUE),
new PairSD("0x1.fffffffffffff4p1023", Double.MAX_VALUE),
new PairSD("0x1.fffffffffffff7fffffp1023", Double.MAX_VALUE),
new PairSD("0x1.fffffffffffff8p1023", infinityD),
new PairSD("0x1.fffffffffffff8000001p1023", infinityD),
new PairSD("0x1.ffffffffffffep1023", Math.nextDown(Double.MAX_VALUE)),
new PairSD("0x1.ffffffffffffe0000p1023", Math.nextDown(Double.MAX_VALUE)),
new PairSD("0x1.ffffffffffffe8p1023", Math.nextDown(Double.MAX_VALUE)),
new PairSD("0x1.ffffffffffffe7p1023", Math.nextDown(Double.MAX_VALUE)),
new PairSD("0x1.ffffffffffffeffffffp1023", Double.MAX_VALUE),
new PairSD("0x1.ffffffffffffe8000001p1023", Double.MAX_VALUE),
};
for (int i = 0; i < testCases.length; i++) {
failures += testCase(testCases[i].s,testCases[i].d);
}
failures += significandAlignmentTests();
{
java.util.Random rand = RandomFactory.getRandom();
// Consistency check; double => hexadecimal => double
// preserves the original value.
for(int i = 0; i < 1000; i++) {
double d = rand.nextDouble();
failures += testCase(Double.toHexString(d), d);
}
}
return failures;
}
/*
* Verify rounding works the same regardless of how the
* significand is aligned on input. A useful extension could be
* to have this sort of test for strings near the overflow
* threshold.
*/
static int significandAlignmentTests() {
int failures = 0;
// baseSignif * 2^baseExp = nextDown(2.0)
long [] baseSignifs = {
0x1ffffffffffffe00L,
0x1fffffffffffff00L
};
double [] answers = {
Math.nextDown(Math.nextDown(2.0)),
Math.nextDown(2.0),
2.0
};
int baseExp = -60;
int count = 0;
for(int i = 0; i < 2; i++) {
for(long j = 0; j <= 0xfL; j++) {
for(long k = 0; k <= 8; k+= 4) { // k = {0, 4, 8}
long base = baseSignifs[i];
long testValue = base | (j<<4) | k;
int offset = 0;
// Calculate when significand should be incremented
// see table 4.7 in Koren book
if ((base & 0x100L) == 0L ) { // lsb is 0
if ( (j >= 8L) && // round is 1
((j & 0x7L) != 0 || k != 0 ) ) // sticky is 1
offset = 1;
}
else { // lsb is 1
if (j >= 8L) // round is 1
offset = 1;
}
double expected = answers[i+offset];
for(int m = -2; m <= 3; m++) {
count ++;
// Form equal value string and evaluate it
String s = "0x" +
Long.toHexString((m >=0) ?(testValue<<m):(testValue>>(-m))) +
"p" + (baseExp - m);
failures += testCase(s, expected);
}
}
}
}
return failures;
}
/*
* Test tricky float rounding cases. The code which
* reads in a hex string converts the string to a double value.
* If a float value is needed, the double value is cast to float.
* However, the cast be itself not always guaranteed to return the
* right result since:
*
* 1. hex string => double can discard a sticky bit which would
* influence a direct hex string => float conversion.
*
* 2. hex string => double => float can have a rounding to double
* precision which results in a larger float value while a direct
* hex string => float conversion would not round up.
*
* This method includes tests of the latter two possibilities.
*/
static int floatTests(){
int failures = 0;
/*
* A String, float pair
*/
class PairSD {
public String s;
public float f;
PairSD(String s, float f) {
this.s = s;
this.f = f;
}
}
String [][] roundingTestCases = {
// Target float value hard rouding version
{"0x1.000000p0", "0x1.0000000000001p0"},
// Try some values that should round up to nextUp(1.0f)
{"0x1.000002p0", "0x1.0000010000001p0"},
{"0x1.000002p0", "0x1.00000100000008p0"},
{"0x1.000002p0", "0x1.0000010000000fp0"},
{"0x1.000002p0", "0x1.00000100000001p0"},
{"0x1.000002p0", "0x1.00000100000000000000000000000000000000001p0"},
{"0x1.000002p0", "0x1.0000010000000fp0"},
// Potential double rounding cases
{"0x1.000002p0", "0x1.000002fffffffp0"},
{"0x1.000002p0", "0x1.000002fffffff8p0"},
{"0x1.000002p0", "0x1.000002ffffffffp0"},
{"0x1.000002p0", "0x1.000002ffff0ffp0"},
{"0x1.000002p0", "0x1.000002ffff0ff8p0"},
{"0x1.000002p0", "0x1.000002ffff0fffp0"},
{"0x1.000000p0", "0x1.000000fffffffp0"},
{"0x1.000000p0", "0x1.000000fffffff8p0"},
{"0x1.000000p0", "0x1.000000ffffffffp0"},
{"0x1.000000p0", "0x1.000000ffffffep0"},
{"0x1.000000p0", "0x1.000000ffffffe8p0"},
{"0x1.000000p0", "0x1.000000ffffffefp0"},
// Float subnormal cases
{"0x0.000002p-126", "0x0.0000010000001p-126"},
{"0x0.000002p-126", "0x0.00000100000000000001p-126"},
{"0x0.000006p-126", "0x0.0000050000001p-126"},
{"0x0.000006p-126", "0x0.00000500000000000001p-126"},
{"0x0.0p-149", "0x0.7ffffffffffffffp-149"},
{"0x1.0p-148", "0x1.3ffffffffffffffp-148"},
{"0x1.cp-147", "0x1.bffffffffffffffp-147"},
{"0x1.fffffcp-127", "0x1.fffffdffffffffp-127"},
};
String [] signs = {"", "-"};
for(int i = 0; i < roundingTestCases.length; i++) {
for(int j = 0; j < signs.length; j++) {
String expectedIn = signs[j]+roundingTestCases[i][0];
String resultIn = signs[j]+roundingTestCases[i][1];
float expected = Float.parseFloat(expectedIn);
float result = Float.parseFloat(resultIn);
if( Float.compare(expected, result) != 0) {
failures += 1;
System.err.println("" + (i+1));
System.err.println("Expected = " + Float.toHexString(expected));
System.err.println("Rounded = " + Float.toHexString(result));
System.err.println("Double = " + Double.toHexString(Double.parseDouble(resultIn)));
System.err.println("Input = " + resultIn);
System.err.println("");
}
}
}
return failures;
}
public static void main(String argv[]) {
int failures = 0;
failures += doubleTests();
failures += floatTests();
if (failures != 0) {
throw new RuntimeException("" + failures + " failures while " +
"testing hexadecimal floating-point " +
"parsing.");
}
}
}

View file

@ -0,0 +1,273 @@
/*
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, Alibaba Group Holding Limited. 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.
*/
/*
* @test
* @bug 4826774 4926547
* @summary Tests for {Float, Double}.toHexString methods
* @library ../Math
* @build DoubleConsts
* @run main ToHexString
*/
import java.util.regex.*;
public class ToHexString {
private ToHexString() {}
/*
* Given a double value, create a hexadecimal floating-point
* string via an intermediate long hex string.
*/
static String doubleToHexString(double d) {
return hexLongStringtoHexDoubleString(Long.toHexString(Double.doubleToLongBits(d)));
}
/*
* Transform the hexadecimal long output into the equivalent
* hexadecimal double value.
*/
static String hexLongStringtoHexDoubleString(String transString) {
transString = transString.toLowerCase();
String zeros = "";
StringBuffer result = new StringBuffer(24);
for(int i = 0; i < (16 - transString.length()); i++, zeros += "0");
transString = zeros + transString;
// assert transString.length == 16;
char topChar;
// Extract sign
if((topChar=transString.charAt(0)) >= '8' ) {// 8, 9, a, A, b, B, ...
result.append("-");
// clear sign bit
transString =
Character.toString(Character.forDigit(Character.digit(topChar, 16) - 8, 16)) +
transString.substring(1,16);
}
// check for NaN and infinity
String signifString = transString.substring(3,16);
if( transString.substring(0,3).equals("7ff") ) {
if(signifString.equals("0000000000000")) {
result.append("Infinity");
}
else
result.append("NaN");
}
else { // finite value
// Extract exponent
int exponent = Integer.parseInt(transString.substring(0,3), 16) -
DoubleConsts.EXP_BIAS;
result.append("0x");
if (exponent == Double.MIN_EXPONENT - 1) { // zero or subnormal
if(signifString.equals("0000000000000")) {
result.append("0.0p0");
}
else {
result.append("0." + signifString.replaceFirst("0+$", "").replaceFirst("^$", "0") +
"p-1022");
}
}
else { // normal value
result.append("1." + signifString.replaceFirst("0+$", "").replaceFirst("^$", "0") +
"p" + exponent);
}
}
return result.toString();
}
public static int toHexStringTests() {
int failures = 0;
String [][] testCases1 = {
{"Infinity", "Infinity"},
{"-Infinity", "-Infinity"},
{"NaN", "NaN"},
{"-NaN", "NaN"},
{"0.0", "0x0.0p0"},
{"-0.0", "-0x0.0p0"},
{"1.0", "0x1.0p0"},
{"-1.0", "-0x1.0p0"},
{"2.0", "0x1.0p1"},
{"3.0", "0x1.8p1"},
{"0.5", "0x1.0p-1"},
{"0.25", "0x1.0p-2"},
{"1.7976931348623157e+308", "0x1.fffffffffffffp1023"}, // MAX_VALUE
{"2.2250738585072014E-308", "0x1.0p-1022"}, // MIN_NORMAL
{"2.225073858507201E-308", "0x0.fffffffffffffp-1022"}, // MAX_SUBNORMAL
{"4.9e-324", "0x0.0000000000001p-1022"} // MIN_VALUE
};
// Compare decimal string -> double -> hex string to hex string
for (int i = 0; i < testCases1.length; i++) {
String result;
if(! (result=Double.toHexString(Double.parseDouble(testCases1[i][0]))).
equals(testCases1[i][1])) {
failures ++;
System.err.println("For floating-point string " + testCases1[i][0] +
", expected hex output " + testCases1[i][1] + ", got " + result +".");
}
}
// Except for float subnormals, the output for numerically
// equal float and double values should be the same.
// Therefore, we will explicitly test float subnormal values.
String [][] floatTestCases = {
{"Infinity", "Infinity"},
{"-Infinity", "-Infinity"},
{"NaN", "NaN"},
{"-NaN", "NaN"},
{"0.0", "0x0.0p0"},
{"-0.0", "-0x0.0p0"},
{"1.0", "0x1.0p0"},
{"-1.0", "-0x1.0p0"},
{"2.0", "0x1.0p1"},
{"3.0", "0x1.8p1"},
{"0.5", "0x1.0p-1"},
{"0.25", "0x1.0p-2"},
{"3.4028235e+38f", "0x1.fffffep127"}, // MAX_VALUE
{"1.17549435E-38f", "0x1.0p-126"}, // MIN_NORMAL
{"1.1754942E-38", "0x0.fffffep-126"}, // MAX_SUBNORMAL
{"1.4e-45f", "0x0.000002p-126"} // MIN_VALUE
};
// Compare decimal string -> double -> hex string to hex string
for (int i = 0; i < floatTestCases.length; i++) {
String result;
if(! (result=Float.toHexString(Float.parseFloat(floatTestCases[i][0]))).
equals(floatTestCases[i][1])) {
failures++;
System.err.println("For floating-point string " + floatTestCases[i][0] +
", expected hex output\n" + floatTestCases[i][1] + ", got\n" + result +".");
}
}
// Particular floating-point values and hex equivalents, mostly
// taken from fdlibm source.
String [][] testCases2 = {
{"+0.0", "0000000000000000"},
{"-0.0", "8000000000000000"},
{"+4.9e-324", "0000000000000001"},
{"-4.9e-324", "8000000000000001"},
// Test cases for trailing zeros in significand
// These test the removal of trailing zeros in the hexadecimal representation
// The comments indicate the number of trailing zeros removed from the significand
// For "0x1.0p1", there are 13 trailing zeros in the significand, but only 12 are removed
// as we always keep at least one hex digit in the significand
{"0x1.0p1", "4000000000000000"}, // 12 trailing zeros removed (13 total, but only 12 removed)
{"0x1.1p1", "4001000000000000"}, // 12 trailing zeros removed (all zeros after '1')
{"0x1.01p1", "4000100000000000"}, // 11 trailing zeros removed
{"0x1.001p1", "4000010000000000"}, // 10 trailing zeros removed
{"0x1.0001p1", "4000001000000000"}, // 9 trailing zeros removed
{"0x1.00001p1", "4000000100000000"}, // 8 trailing zeros removed
{"0x1.000001p1", "4000000010000000"}, // 7 trailing zeros removed
{"0x1.0000001p1", "4000000001000000"}, // 6 trailing zeros removed
{"0x1.00000001p1", "4000000000100000"}, // 5 trailing zeros removed
{"0x1.000000001p1", "4000000000010000"}, // 4 trailing zeros removed
{"0x1.0000000001p1", "4000000000001000"}, // 3 trailing zeros removed
{"0x1.00000000001p1", "4000000000000100"}, // 2 trailing zeros removed
{"0x1.000000000001p1", "4000000000000010"}, // 1 trailing zero removed (minimum)
{"0x1.0000000000001p1", "4000000000000001"}, // 0 trailing zeros removed (no trailing zeros to remove)
// fdlibm k_sin.c
{"+5.00000000000000000000e-01", "3FE0000000000000"},
{"-1.66666666666666324348e-01", "BFC5555555555549"},
{"+8.33333333332248946124e-03", "3F8111111110F8A6"},
{"-1.98412698298579493134e-04", "BF2A01A019C161D5"},
{"+2.75573137070700676789e-06", "3EC71DE357B1FE7D"},
{"-2.50507602534068634195e-08", "BE5AE5E68A2B9CEB"},
{"+1.58969099521155010221e-10", "3DE5D93A5ACFD57C"},
// fdlibm k_cos.c
{"+4.16666666666666019037e-02", "3FA555555555554C"},
{"-1.38888888888741095749e-03", "BF56C16C16C15177"},
{"+2.48015872894767294178e-05", "3EFA01A019CB1590"},
{"-2.75573143513906633035e-07", "BE927E4F809C52AD"},
{"+2.08757232129817482790e-09", "3E21EE9EBDB4B1C4"},
{"-1.13596475577881948265e-11", "BDA8FAE9BE8838D4"},
// fdlibm e_rempio.c
{"1.67772160000000000000e+07", "4170000000000000"},
{"6.36619772367581382433e-01", "3FE45F306DC9C883"},
{"1.57079632673412561417e+00", "3FF921FB54400000"},
{"6.07710050650619224932e-11", "3DD0B4611A626331"},
{"6.07710050630396597660e-11", "3DD0B4611A600000"},
{"2.02226624879595063154e-21", "3BA3198A2E037073"},
{"2.02226624871116645580e-21", "3BA3198A2E000000"},
{"8.47842766036889956997e-32", "397B839A252049C1"},
// fdlibm s_cbrt.c
{"+5.42857142857142815906e-01", "3FE15F15F15F15F1"},
{"-7.05306122448979611050e-01", "BFE691DE2532C834"},
{"+1.41428571428571436819e+00", "3FF6A0EA0EA0EA0F"},
{"+1.60714285714285720630e+00", "3FF9B6DB6DB6DB6E"},
{"+3.57142857142857150787e-01", "3FD6DB6DB6DB6DB7"},
};
// Compare decimal string -> double -> hex string to
// long hex string -> double hex string
for (int i = 0; i < testCases2.length; i++) {
String result;
String expected;
if(! (result=Double.toHexString(Double.parseDouble(testCases2[i][0]))).
equals( expected=hexLongStringtoHexDoubleString(testCases2[i][1]) )) {
failures ++;
System.err.println("For floating-point string " + testCases2[i][0] +
", expected hex output " + expected + ", got " + result +".");
}
}
// Test random double values;
// compare double -> Double.toHexString with local doubleToHexString
java.util.Random rand = new java.util.Random(0);
for (int i = 0; i < 1000; i++) {
String result;
String expected;
double d = rand.nextDouble();
if(! (expected=doubleToHexString(d)).equals(result=Double.toHexString(d)) ) {
failures ++;
System.err.println("For floating-point value " + d +
", expected hex output " + expected + ", got " + result +".");
}
}
return failures;
}
public static void main(String argv[]) {
int failures = 0;
failures = toHexStringTests();
if (failures != 0) {
throw new RuntimeException("" + failures + " failures while testing Double.toHexString");
}
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2009, 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.
*/
/*
* @test
* @bug 4428022
* @summary Tests for Double.toString
* @author Andrew Haley <aph@redhat.com>
*/
public class ToString {
public static void main(String args[]) {
if (!Double.toString(0.001).equals("0.001"))
throw new RuntimeException("Double.toString(0.001) is not \"0.001\"");
if (!Double.toString(0.002).equals("0.002"))
throw new RuntimeException("Double.toString(0.001) is not \"0.002\"");
}
}