-
Bug
-
Resolution: Fixed
-
P4
-
8, 11, 17, 22, 23
-
b26
-
generic
-
generic
-
Verified
A DESCRIPTION OF THE PROBLEM :
Using the class DecimalFormat to parse numbers in scientific notation with large exponents leads to unexpected results.
Examples:
- Parsing the string "0.123E2147483648" should produce the +infinity value. However, parse() returns 0.
- Parsing the string "0.123E-2147483649" should produce 0. However, parse() returns +infinity.
- Parsing the string "0.123E4294967296" should produce +infinity. However, parse() returns 0.123.
There seems to be an overflow of the exponent value while parsing the strings.
DecimalFormat uses the class DigitList, which has the int field decimalAt. According to the JavaDoc of decimalAt the numerical value of the input is determined by the formula f * 10^decimalAt, where f is a value 0.1 <= f < 1.
decimalAt overflows exactly at the range bounds of the integer type if the mantissa is in the range of 0.1 and 1. If the mantissa is not in that specific range, the overflow of decimalAt will happen at an offset determined by the value of the mantissa.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Parse a number in scientific notation (mantissa in the range of 0.1 and 1) with an exponent that exceeds the range of an integer.
Example: "0.123E2147483648"
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
If the exponent is > Integer.MAX_VALUE, plus or minus infinity.
If the exponent is < Integer.MIN_VALUE, plus or minus 0.
Example: "0.123E2147483648" => expected result is +infinity
ACTUAL -
Depends on the actual value of the exponent
Example: "0.123E2147483648" => actual result is 0
---------- BEGIN SOURCE ----------
package org.example;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
public class Main {
static final NumberFormat numberFormat = NumberFormat.getInstance(Locale.US);
static void parse(final String s) throws ParseException {
final Number parseResult = numberFormat.parse(s);
System.out.println(STR."numberFormat.parse(\"\{s}\") -> \{parseResult}");
}
public static void main(final String[] args) throws ParseException {
// Making sure we're dealing with DecimalFormat
if (numberFormat.getClass() != DecimalFormat.class) {
throw new AssertionError("Did not get a DecimalFormat");
}
parse("0.123E1"); // Ok
parse("0.123E309"); // Ok
parse("0.123E310"); // Ok, this exceeds the range of double and results in +INFINITY
parse("0.123E2147483647"); // Ok, this exceeds the range of double and results in +INFINITY; exponent is Integer.MAX_VALUE
parse("0.123E2147483648"); // NOT ok, result is 0, but should be +INFINITY; exponent is Integer.MAX_VALUE + 1
parse("0.0123E2147483648"); // Ok, results in +INFINITY; exponent is Integer.MAX_VALUE + 1, but mantissa is between 0.01 and 0.1
parse("0.0123E2147483649"); // NOT ok, result is 0, but should be +INFINITY; exponent is Integer.MAX_VALUE + 2 and mantissa is between 0.01 and 0.1
parse("1.23E2147483646"); // Ok, results in +INFINITY; exponent is Integer.MAX_VALUE - 1 and mantissa is between 1 and 10
parse("1.23E2147483647"); // NOT ok, result is 0, but should be +INFINITY; exponent is Integer.MAX_VALUE, but mantissa is between 1 and 10
parse("0.123E-322"); // Ok
parse("0.123E-323"); // Ok, this exceeds the range of double (Double.MIN_VALUE) and results in 0
parse("0.123E-2147483648"); // Ok, this exceeds the range of double (Double.MIN_VALUE) and results in 0; exponent is Integer.MIN_VALUE
parse("0.123E-2147483649"); // Not ok, result is +INFINITY, but should be 0; exponent is Integer.MIN_VALUE - 1
parse("0.123E4294967296"); // Not ok, result is 0.123, but should be +INFINITY; exponent is FFFF_FFFF + 1
}
}
---------- END SOURCE ----------
FREQUENCY : always
Using the class DecimalFormat to parse numbers in scientific notation with large exponents leads to unexpected results.
Examples:
- Parsing the string "0.123E2147483648" should produce the +infinity value. However, parse() returns 0.
- Parsing the string "0.123E-2147483649" should produce 0. However, parse() returns +infinity.
- Parsing the string "0.123E4294967296" should produce +infinity. However, parse() returns 0.123.
There seems to be an overflow of the exponent value while parsing the strings.
DecimalFormat uses the class DigitList, which has the int field decimalAt. According to the JavaDoc of decimalAt the numerical value of the input is determined by the formula f * 10^decimalAt, where f is a value 0.1 <= f < 1.
decimalAt overflows exactly at the range bounds of the integer type if the mantissa is in the range of 0.1 and 1. If the mantissa is not in that specific range, the overflow of decimalAt will happen at an offset determined by the value of the mantissa.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Parse a number in scientific notation (mantissa in the range of 0.1 and 1) with an exponent that exceeds the range of an integer.
Example: "0.123E2147483648"
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
If the exponent is > Integer.MAX_VALUE, plus or minus infinity.
If the exponent is < Integer.MIN_VALUE, plus or minus 0.
Example: "0.123E2147483648" => expected result is +infinity
ACTUAL -
Depends on the actual value of the exponent
Example: "0.123E2147483648" => actual result is 0
---------- BEGIN SOURCE ----------
package org.example;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
public class Main {
static final NumberFormat numberFormat = NumberFormat.getInstance(Locale.US);
static void parse(final String s) throws ParseException {
final Number parseResult = numberFormat.parse(s);
System.out.println(STR."numberFormat.parse(\"\{s}\") -> \{parseResult}");
}
public static void main(final String[] args) throws ParseException {
// Making sure we're dealing with DecimalFormat
if (numberFormat.getClass() != DecimalFormat.class) {
throw new AssertionError("Did not get a DecimalFormat");
}
parse("0.123E1"); // Ok
parse("0.123E309"); // Ok
parse("0.123E310"); // Ok, this exceeds the range of double and results in +INFINITY
parse("0.123E2147483647"); // Ok, this exceeds the range of double and results in +INFINITY; exponent is Integer.MAX_VALUE
parse("0.123E2147483648"); // NOT ok, result is 0, but should be +INFINITY; exponent is Integer.MAX_VALUE + 1
parse("0.0123E2147483648"); // Ok, results in +INFINITY; exponent is Integer.MAX_VALUE + 1, but mantissa is between 0.01 and 0.1
parse("0.0123E2147483649"); // NOT ok, result is 0, but should be +INFINITY; exponent is Integer.MAX_VALUE + 2 and mantissa is between 0.01 and 0.1
parse("1.23E2147483646"); // Ok, results in +INFINITY; exponent is Integer.MAX_VALUE - 1 and mantissa is between 1 and 10
parse("1.23E2147483647"); // NOT ok, result is 0, but should be +INFINITY; exponent is Integer.MAX_VALUE, but mantissa is between 1 and 10
parse("0.123E-322"); // Ok
parse("0.123E-323"); // Ok, this exceeds the range of double (Double.MIN_VALUE) and results in 0
parse("0.123E-2147483648"); // Ok, this exceeds the range of double (Double.MIN_VALUE) and results in 0; exponent is Integer.MIN_VALUE
parse("0.123E-2147483649"); // Not ok, result is +INFINITY, but should be 0; exponent is Integer.MIN_VALUE - 1
parse("0.123E4294967296"); // Not ok, result is 0.123, but should be +INFINITY; exponent is FFFF_FFFF + 1
}
}
---------- END SOURCE ----------
FREQUENCY : always
- relates to
-
JDK-8331680 NumberFormat is missing some bad exponent strict parse cases
- Resolved