Fastest way to check if a String can be parsed to Double in Java

22,659

Solution 1

You can check it using the same regular expression the Double class uses. It's well documented here:

http://docs.oracle.com/javase/6/docs/api/java/lang/Double.html#valueOf%28java.lang.String%29

Here is the code part:

To avoid calling this method on an invalid string and having a NumberFormatException be thrown, the regular expression below can be used to screen the input string:

  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 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"

  if (Pattern.matches(fpRegex, myString))
            Double.valueOf(myString); // Will not throw NumberFormatException
        else {
            // Perform suitable alternative action
        }

Solution 2

There is a handy NumberUtils#isNumber in Apache Commons Lang. It is a bit far fetched:

Valid numbers include hexadecimal marked with the 0x qualifier, scientific notation and numbers marked with a type qualifier (e.g. 123L).

but I guess it might be faster than regular expressions or throwing and catching an exception.

Solution 3

The Apache Commons NumberUtil is actually quite fast. I'm guessing it's way faster than any regexp implementation.

Solution 4

I use the following code to check if a string can be parsed to double:

public static boolean isDouble(String str) {
    if (str == null) {
        return false;
    }
    int length = str.length();
    if (length == 0) {
        return false;
    }
    int i = 0;
    if (str.charAt(0) == '-') {
        if (length == 1) {
            return false;
        }
        ++i;
    }
    int integerPartSize = 0;
    int exponentPartSize = -1;
    while (i < length) {
        char c = str.charAt(i);
        if (c < '0' || c > '9') {
            if (c == '.' && integerPartSize > 0 && exponentPartSize == -1) {
                exponentPartSize = 0;
            } else {
                return false;
            }
        } else if (exponentPartSize > -1) {
            ++exponentPartSize;
        } else {
            ++integerPartSize;
        }
        ++i;
    }
    if ((str.charAt(0) == '0' && i > 1 && exponentPartSize < 1)
            || exponentPartSize == 0 || (str.charAt(length - 1) == '.')) {
        return false;
    }
    return true;
}

I am aware that the output is not exactly the same as for the regular expression in the Double class but this method is much faster and the result is good enough for my needs. These are my unit tests for the method.

@Test
public void shouldReturnTrueIfStringIsDouble() {
    assertThat(Utils.isDouble("0.0")).isTrue();
    assertThat(Utils.isDouble("0.1")).isTrue();
    assertThat(Utils.isDouble("-0.0")).isTrue();
    assertThat(Utils.isDouble("-0.1")).isTrue();
    assertThat(Utils.isDouble("1.0067890")).isTrue();
    assertThat(Utils.isDouble("0")).isTrue();
    assertThat(Utils.isDouble("1")).isTrue();
}

@Test
public void shouldReturnFalseIfStringIsNotDouble() {
    assertThat(Utils.isDouble(".01")).isFalse();
    assertThat(Utils.isDouble("0.1f")).isFalse();
    assertThat(Utils.isDouble("a")).isFalse();
    assertThat(Utils.isDouble("-")).isFalse();
    assertThat(Utils.isDouble("-1.")).isFalse();
    assertThat(Utils.isDouble("-.1")).isFalse();
    assertThat(Utils.isDouble("123.")).isFalse();
    assertThat(Utils.isDouble("1.2.3")).isFalse();
    assertThat(Utils.isDouble("1,3")).isFalse();
}
Share:
22,659
Chad
Author by

Chad

Updated on July 05, 2022

Comments

  • Chad
    Chad almost 2 years

    I know there's a million ways of doing this but what is the fastest? This should include scientific notation.

    NOTE: I'm not interested in converting the value to Double, i'm only interested in knowing if it's possible. i.e. private boolean isDouble(String value).

  • corsiKa
    corsiKa over 12 years
    You can't rely on doing it once to determine a benchmark. There's just too much variation that could happen, and you don't know the resolution of the milli's clock.
  • Paul
    Paul over 12 years
    Have you looked at the source code for that method? I don't see why it would be any faster than a regular expression - it's a jumble of loops, comparisons, flags...probably what goes on under the hood with a regex but it's sure ugly to look at.
  • HRgiger
    HRgiger over 12 years
    @glowcoder You are right too many possible variation, also maybe hardware. About milli`s: isnt it a long value including all millis since 1.1.1970?
  • Paul
    Paul over 12 years
    What @glowcoder said - do it a million times with a pre-compiled pattern and get back to us.
  • Paul
    Paul over 12 years
    Try using System.nanoTime() instead of currentTimeMillis().
  • corsiKa
    corsiKa over 12 years
    Yes in Java it's milli's from epoch. But that's not what I mean by resolution. Consider the following: ideone.com/KOOP3 Notice how the time milli's go up by 1? Now copy that code and run it on your machine. On mine they go up by between 15-16 per tick.
  • Tomasz Nurkiewicz
    Tomasz Nurkiewicz over 12 years
    @Paul: I had a quick look there (I regret now ;-)) but as long as it works, I don't care. I also don't know whether it will be faster than a regular expression. Remember that regex is a dynamically generated state-machine (although probably very optimized).
  • Chad
    Chad over 12 years
    Actually in my case the fastest solution was to just if-else through the whole String using flags and whatnots. But that's because in my case the String is most often really, small (like 3 or 4 characters). As a general solution though, i think this is the best.
  • HRgiger
    HRgiger over 12 years
    @glowcoder @ Paul yep clear now:) sorry for misunderstanding, it would be another topic. also thank you for the new information!
  • joergl
    joergl over 11 years
    Can you provide a benchmark that replaces this guess with hard facts?
  • David Dossot
    David Dossot over 11 years
    Also I see isDigits and isNumber in org.apache.commons.lang.math.NumberUtils, but nothing to check for isDouble. So what method were you suggesting to use?
  • Chad
    Chad about 11 years
    How is throwing and catching fast? Not to mention bad practice? And using a period is not locale safe.
  • Seega
    Seega over 10 years
    isNumber checks for all numbers (look at the docu...) Valid numbers include hexadecimal marked with the 0x qualifier, scientific notation and numbers marked with a type qualifier (e.g. 123L)
  • Asu
    Asu about 7 years
    If you only need doubles, you would not want your validator to return true for all other kinds of numbers.
  • Martin Holland
    Martin Holland over 6 years
    Thank you! I've implement this method instead of the reg exp version and had a massive performance improvement. using a java profiler I can see that I've gone from 27,000ms just on calls to the reg exp isDouble function to 97ms using yours - with same number of calls.