Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a001c19
Add testLang1641()
garydgregory Jul 19, 2024
abb0ca4
Rename some test methods
garydgregory Jul 19, 2024
73e4c9d
Merge remote-tracking branch 'upstream/master'
garydgregory Jul 19, 2024
0bcc867
Merge remote-tracking branch 'upstream/master'
garydgregory Jul 20, 2024
3484d8a
Merge remote-tracking branch 'upstream/master'
garydgregory Nov 3, 2025
dec6a36
Merge remote-tracking branch 'upstream/master'
garydgregory Nov 5, 2025
d959b47
Merge remote-tracking branch 'upstream/master'
garydgregory Nov 11, 2025
78cfdd3
Merge remote-tracking branch 'upstream/master'
garydgregory Nov 17, 2025
dddab49
Merge remote-tracking branch 'upstream/master'
garydgregory Nov 18, 2025
550d4b9
Merge remote-tracking branch 'upstream/master'
garydgregory Nov 25, 2025
22e4e5e
Merge remote-tracking branch 'upstream/master'
garydgregory Nov 27, 2025
a5bcad2
Merge remote-tracking branch 'upstream/master'
garydgregory Jan 9, 2026
9c606b5
Merge remote-tracking branch 'upstream/master'
garydgregory Jan 9, 2026
17d776d
Merge remote-tracking branch 'upstream/master'
garydgregory Jan 9, 2026
cddcb72
Merge remote-tracking branch 'upstream/master'
garydgregory Jan 10, 2026
d7a972c
Merge remote-tracking branch 'upstream/master'
garydgregory Jan 25, 2026
1309d96
Merge remote-tracking branch 'upstream/master'
garydgregory Feb 21, 2026
102fcb0
Merge remote-tracking branch 'upstream/master'
garydgregory Apr 15, 2026
7522574
Merge remote-tracking branch 'upstream/master'
garydgregory Apr 15, 2026
0739ddb
NumberUtils.isCreatable(String) should match
garydgregory Apr 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 5 additions & 100 deletions src/main/java/org/apache/commons/lang3/math/NumberUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import java.util.Objects;
import java.util.function.Consumer;

import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

Expand Down Expand Up @@ -109,7 +108,7 @@ private static <T> boolean accept(final Consumer<T> consumer, final T obj) {
try {
consumer.accept(obj);
return true;
} catch (Exception e) {
} catch (final Exception e) {
return false;
}
}
Expand Down Expand Up @@ -584,106 +583,12 @@ public static boolean isCreatable(final String str) {
if (StringUtils.isEmpty(str)) {
return false;
}
final char[] chars = str.toCharArray();
int sz = chars.length;
boolean hasExp = false;
boolean hasDecPoint = false;
boolean allowSigns = false;
boolean foundDigit = false;
// deal with any possible sign up front
final int start = isSign(chars[0]) ? 1 : 0;
if (sz > start + 1 && chars[start] == '0' && !StringUtils.contains(str, '.')) { // leading 0, skip if is a decimal number
if (chars[start + 1] == 'x' || chars[start + 1] == 'X') { // leading 0x/0X
int i = start + 2;
if (i == sz) {
return false; // str == "0x"
}
// checking hex (it can't be anything else)
for (; i < chars.length; i++) {
if (!CharUtils.isHex(chars[i])) {
return false;
}
}
return true;
}
if (Character.isDigit(chars[start + 1])) {
// leading 0, but not hex, must be octal
int i = start + 1;
for (; i < chars.length; i++) {
if (!CharUtils.isOctal(chars[i])) {
return false;
}
}
return true;
}
}
sz--; // don't want to loop to the last char, check it afterwards
// for type qualifiers
int i = start;
// 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) {
if (CharUtils.isAsciiNumeric(chars[i])) {
foundDigit = true;
allowSigns = false;
} else if (chars[i] == '.') {
if (hasDecPoint || hasExp) {
// two decimal points or dec in exponent
return false;
}
hasDecPoint = true;
} else if (chars[i] == 'e' || chars[i] == 'E') {
// we've already taken care of hex.
if (hasExp) {
// two E's
return false;
}
if (!foundDigit) {
return false;
}
hasExp = true;
allowSigns = true;
} else if (isSign(chars[i])) {
if (!allowSigns) {
return false;
}
allowSigns = false;
foundDigit = false; // we need a digit after the E
} else {
return false;
}
i++;
}
if (i < chars.length) {
if (CharUtils.isAsciiNumeric(chars[i])) {
// no type qualifier, OK
return true;
}
if (chars[i] == 'e' || chars[i] == 'E') {
// can't have an E at the last byte
return false;
}
if (chars[i] == '.') {
if (hasDecPoint || hasExp) {
// two decimal points or dec in exponent
return false;
}
// single trailing decimal point after non-exponent is ok
return foundDigit;
}
if (!allowSigns && (chars[i] == 'd' || chars[i] == 'D' || chars[i] == 'f' || chars[i] == 'F')) {
return foundDigit;
}
if (chars[i] == 'l' || chars[i] == 'L') {
// not allowing L with an exponent or decimal point
return foundDigit && !hasExp && !hasDecPoint;
}
// last character is illegal
try {
createNumber(str);
return true;
} catch (final RuntimeException e) {
return false;
}
// allowSigns is true iff the val ends in 'E'
// found digit it to make sure weird stuff like '.' and '1E-' doesn't pass
return !allowSigns && foundDigit;
}

/**
Expand Down
22 changes: 20 additions & 2 deletions src/test/java/org/apache/commons/lang3/math/NumberUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -54,8 +56,7 @@ private static void assertCreateNumberZero(final String number, final Object zer

private boolean checkCreateNumber(final String val) {
try {
final Object obj = NumberUtils.createNumber(val);
return obj != null;
return NumberUtils.createNumber(val) != null;
} catch (final NumberFormatException e) {
return false;
}
Expand Down Expand Up @@ -887,6 +888,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
Expand Down
Loading