diff --git a/src/main/java/org/apache/commons/lang3/math/NumberUtils.java b/src/main/java/org/apache/commons/lang3/math/NumberUtils.java index d4863e746be..4910db6a590 100644 --- a/src/main/java/org/apache/commons/lang3/math/NumberUtils.java +++ b/src/main/java/org/apache/commons/lang3/math/NumberUtils.java @@ -24,7 +24,9 @@ import java.util.function.Consumer; import org.apache.commons.lang3.CharUtils; +import org.apache.commons.lang3.JavaVersion; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.Validate; /** @@ -620,6 +622,7 @@ public static boolean isCreatable(final String str) { sz--; // don't want to loop to the last char, check it afterwards // for type qualifiers int i = start; + int expPos = -1; // loop to the next to last char or to the last char if we need another digit to // make a valid number (e.g. chars[0..5] = "1234E") while (i < sz || i < sz + 1 && allowSigns && !foundDigit) { @@ -643,6 +646,7 @@ public static boolean isCreatable(final String str) { } hasExp = true; allowSigns = true; + expPos = i; } else if (isSign(chars[i])) { if (!allowSigns) { return false; @@ -654,6 +658,27 @@ public static boolean isCreatable(final String str) { } i++; } + if (expPos > -1 && expPos < chars.length - 1) { + int expEndPos = chars.length; + final char lastChar = chars[expEndPos - 1]; + if (lastChar == 'd' || lastChar == 'D' + || lastChar == 'f' || lastChar == 'F' + || lastChar == 'l' || lastChar == 'L') { + expEndPos--; + } + final String expStr = str.substring(expPos + 1, expEndPos); + try { + final int exp = Integer.parseInt(expStr); + if (exp == Integer.MIN_VALUE) { + return false; + } + } catch (final NumberFormatException ignored) { + // Largest positive exponent of Integer.MAX_VALUE + 1 which is supported by Java 21 and later. + final String largestPositiveExponent = "2147483648"; + final boolean isLargestPositiveExponent = largestPositiveExponent.equals(expStr) || ("+" + largestPositiveExponent).equals(expStr); + return isLargestPositiveExponent && SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21); + } + } if (i < chars.length) { if (CharUtils.isAsciiNumeric(chars[i])) { // no type qualifier, OK diff --git a/src/test/java/org/apache/commons/lang3/math/NumberUtilsTest.java b/src/test/java/org/apache/commons/lang3/math/NumberUtilsTest.java index a41842c0d21..22a85265bc2 100644 --- a/src/test/java/org/apache/commons/lang3/math/NumberUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/math/NumberUtilsTest.java @@ -36,7 +36,9 @@ import java.util.function.Function; import org.apache.commons.lang3.AbstractLangTest; +import org.apache.commons.lang3.JavaVersion; import org.apache.commons.lang3.SystemProperties; +import org.apache.commons.lang3.SystemUtils; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -887,6 +889,23 @@ void testIsCreatable() { compareIsCreatableWithCreateNumber(".D", false); // LANG-1646 compareIsCreatableWithCreateNumber(".e10", false); // LANG-1646 compareIsCreatableWithCreateNumber(".e10D", false); // LANG-1646 + compareIsCreatableWithCreateNumber("1E2147483647", true); + compareIsCreatableWithCreateNumber("1E+2147483647", true); + compareIsCreatableWithCreateNumber("1E-2147483647", true); + compareIsCreatableWithCreateNumber("1E-2147483648", false); + compareIsCreatableWithCreateNumber("1E2147483648", SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21)); + compareIsCreatableWithCreateNumber("1E+2147483648", SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21)); + compareIsCreatableWithCreateNumber("1E+2147483649", false); + compareIsCreatableWithCreateNumber("1E-2147483649", false); + compareIsCreatableWithCreateNumber("1E2147483648D", SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21)); + compareIsCreatableWithCreateNumber("1E-2147483648D", false); + compareIsCreatableWithCreateNumber("1E2147483648F", SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21)); + compareIsCreatableWithCreateNumber("1E+2147483648F", SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21)); + compareIsCreatableWithCreateNumber("1E-2147483648F", false); + compareIsCreatableWithCreateNumber("1.0E2147483648", SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_21)); + compareIsCreatableWithCreateNumber("1.0E-2147483648", false); + compareIsCreatableWithCreateNumber("1E+999999999999999999999", false); + compareIsCreatableWithCreateNumber("1E-999999999999999999999", false); } @Test