This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


The following commit(s) were added to refs/heads/master by this push:
     new 127050d2b NumberUtils.isCreatable(String) should match 
NumberUtils.createNumber(String), exactly (#1626)
127050d2b is described below

commit 127050d2b54bd6224bc53573228e8774dc7be28b
Author: Gary Gregory <[email protected]>
AuthorDate: Thu Apr 16 09:29:30 2026 -0400

    NumberUtils.isCreatable(String) should match 
NumberUtils.createNumber(String), exactly (#1626)
    
    * Add testLang1641()
    
    * Rename some test methods
    
    * NumberUtils.isCreatable(String) should match
    NumberUtils.createNumber(String), exactly
    
    Add tests from PR #1623
---
 .../org/apache/commons/lang3/math/NumberUtils.java | 105 +--------------------
 .../apache/commons/lang3/math/NumberUtilsTest.java |  22 ++++-
 2 files changed, 25 insertions(+), 102 deletions(-)

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 d4863e746..7327fac06 100644
--- a/src/main/java/org/apache/commons/lang3/math/NumberUtils.java
+++ b/src/main/java/org/apache/commons/lang3/math/NumberUtils.java
@@ -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;
 
@@ -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;
         }
     }
@@ -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;
     }
 
     /**
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 a41842c0d..0cd9105af 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;
@@ -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;
         }
@@ -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

Reply via email to