28

In Java, I am trying to find out if the value contained in a string is double or not?

Gauranga
  • 323
  • 1
  • 4
  • 9
  • 2
    What are your criteria for being a double? For example, if your String is `100` it would be a double, but also a long. – corsiKa Jun 28 '10 at 15:36
  • 1
    by "double or not", do you mean a double or an integer or something else? – akf Jun 28 '10 at 15:36
  • 2
    What do you mean by "double"? You mean, it's a floating point number needing double precision (in contrast with single that we could call "float" simply)? Or any number that can be a double? (So, Double.parseDouble won't fail on, say, 5) – ShinTakezou Jun 28 '10 at 15:37
  • 2
    Is the string "Infinity" a double in your opinion? FYI, the `Double.parseDouble` lets it through :-) – aioobe Jun 28 '10 at 15:56
  • To add to other answers/comments: remember that a "string representing a double" can also depend on the locale. Eg, in some locales the comma is the decimal separator. So, be careful in stating unambigously what you pretend to do. – leonbloy Jun 28 '10 at 19:56
  • You could try matching it with a regular expression, as described here: http://www.regular-expressions.info/floatingpoint.html Related questions: https://stackoverflow.com/questions/1295327/regex-to-parse-international-floating-point-numbers https://stackoverflow.com/questions/2293780/how-to-detect-a-floating-point-number-using-a-regular-expression – Ori Pessach Jun 28 '10 at 15:38

14 Answers14

35
    boolean isDouble(String str) {
        try {
            Double.parseDouble(str);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }
unbeli
  • 29,501
  • 5
  • 55
  • 57
  • 8
    This is not entirely valid. E.g. 49d or 49.d returns true here, which is usually not desired in this case. – ulrich Mar 04 '16 at 15:53
  • instead of this can we put like this if(Double.isNaN(Double.parseDouble(str))){ return false; } else { return true; } – Balban Jun 25 '18 at 06:38
16

There is a note about this in the source for Double:

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

The final form of the regular expressions that follows is quite long:

[\x00-\x20]*[+-]?(NaN|Infinity|((((\p{Digit}+)(\.)?((\p{Digit}+)?)([eE][+-]?(\p{Digit}+))?)|(\.((\p{Digit}+))([eE][+-]?(\p{Digit}+))?)|(((0[xX](\p{XDigit}+)(\.)?)|(0[xX](\p{XDigit}+)?(\.)(\p{XDigit}+)))[pP][+-]?(\p{Digit}+)))[fFdD]?))[\x00-\x20]*

Using this method however, you can easily exclude some special doubles such as Infinity and NaN which are both accepted by Double.parseDouble. For example like this:

String regExp = "[\\x00-\\x20]*[+-]?(((((\\p{Digit}+)(\\.)?((\\p{Digit}+)?)([eE][+-]?(\\p{Digit}+))?)|(\\.((\\p{Digit}+))([eE][+-]?(\\p{Digit}+))?)|(((0[xX](\\p{XDigit}+)(\\.)?)|(0[xX](\\p{XDigit}+)?(\\.)(\\p{XDigit}+)))[pP][+-]?(\\p{Digit}+)))[fFdD]?))[\\x00-\\x20]*";
boolean matches = yourString.matches(regExp);
aioobe
  • 413,195
  • 112
  • 811
  • 826
  • I added this to the benchmark test and it was a little slower than the `Double.parseDouble(s)` solution. I imagine just checking for special double values in the guard block before parsing would be the best way to go about this. – Bill the Lizard Jun 28 '10 at 17:44
  • Yes, I agree. The parseDouble seems quite optimized: http://www.docjar.com/html/api/sun/misc/FloatingDecimal.java.html (readJavaFormatString) – aioobe Jun 28 '10 at 19:17
  • 4
    Now that I pre-compiled the regular expression, it's outperforming the `Double.parseDouble(s)` method. – Bill the Lizard Jun 28 '10 at 21:03
  • @aioobe +1 for really deep-diving. – Anand Kadhi Jan 06 '16 at 07:08
10

Using a Scanner will be significantly slower than using Double.parseDouble(String s).

private static Random rand = new Random();
private static final String regExp = "[\\x00-\\x20]*[+-]?(((((\\p{Digit}+)(\\.)?((\\p{Digit}+)?)([eE][+-]?(\\p{Digit}+))?)|(\\.((\\p{Digit}+))([eE][+-]?(\\p{Digit}+))?)|(((0[xX](\\p{XDigit}+)(\\.)?)|(0[xX](\\p{XDigit}+)?(\\.)(\\p{XDigit}+)))[pP][+-]?(\\p{Digit}+)))[fFdD]?))[\\x00-\\x20]*";
private static final Pattern pattern = Pattern.compile(regExp);

public static void main(String[] args) {

    int trials = 50000;
    String[] values = new String[trials];

    // initialize the array
    // about half the values will be parsable as double
    for( int i = 0; i < trials; ++i ) {
        double d = rand.nextDouble();
        boolean b = rand.nextBoolean();

        values[i] = (b ? "" : "abc") + d;
    }

    long start = System.currentTimeMillis();

    int parseCount = 0;
    for( int i = 0; i < trials; ++i ) {
        if( isDoubleParse(values[i]) ) {
            parseCount++;
        }
    }

    long end = System.currentTimeMillis();
    long elapsed = end - start;

    System.out.println("Elapsed time parsing: " + elapsed + " ms");
    System.out.println("Doubles: " + parseCount);

    // reset the timer for the next run
    start = System.currentTimeMillis();

    int scanCount = 0;
    for( int i = 0; i < trials; ++i ) {
        if( isDoubleScan(values[i]) ) {
            scanCount++;
        }
    }

    end = System.currentTimeMillis();
    elapsed = end - start;

    System.out.println("Elapsed time scanning: " + elapsed + " ms");
    System.out.println("Doubles: " + scanCount);


    // reset the timer for the next run
    start = System.currentTimeMillis();

    int regexCount = 0;
    for( int i = 0; i < trials; ++i ) {
        if( isDoubleRegex(values[i]) ) {
            regexCount++;
        }
    }

    end = System.currentTimeMillis();
    elapsed = end - start;

    System.out.println("Elapsed time regex (naive): " + elapsed + " ms");
    System.out.println("Doubles: " + naiveRegexCount);


    // reset the timer for the next run
    start = System.currentTimeMillis();

    int compiledRegexCount = 0;
    for( int i = 0; i < trials; ++i ) {
        if( isDoubleCompiledRegex(values[i]) ) {
            compiledRegexCount++;
        }
    }

    end = System.currentTimeMillis();
    elapsed = end - start;

    System.out.println("Elapsed time regex (compiled): " + elapsed + " ms");
    System.out.println("Doubles: " + compiledRegexCount);
}


public static boolean isDoubleParse(String s) {
    if( s == null ) return false;
    try {
        Double.parseDouble(s);
        return true;
    } catch (NumberFormatException e) {
        return false;
    }
}

public static boolean isDoubleScan(String s) {
    Scanner scanner = new Scanner(s);
    return scanner.hasNextDouble();
}

public static boolean isDoubleRegex(String s) {
    return s.matches(regExp);
}

public static boolean isDoubleCompiledRegex(String s) {
    Matcher m = pattern.matcher(s);
    return m.matches();
}

When I run the code above I get the following output:

Elapsed time parsing: 235 ms
Doubles: 24966
Elapsed time scanning: 31358 ms
Doubles: 24966
Elapsed time regex (naive): 1829 ms
Doubles: 24966
Elapsed time regex (compiled): 109 ms
Doubles: 24966

The regular expression method runs fairly quickly given the complexity of the regex, but still not quite as fast as simply parsing using Double.parseDouble(s). As pointed out in the comments, there are a few values like NaN that get past the parser that probably shouldn't.

Update:

Pre-compiling the regular expression as suggested by @Gabe makes all the difference. The compiled regex method is now the clear winner.

Community
  • 1
  • 1
Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
  • Can you run it again with the regex approach (http://stackoverflow.com/questions/3133770/how-to-find-out-if-the-value-contained-in-a-string-is-double-or-not/3133917#3133917)? – Gabe Jun 28 '10 at 17:31
  • @Gabe: Sure. The regex example wasn't quite as fast as the `Double.parseDouble(s)` example, but I have to question the complexity of that regular expression. ;) – Bill the Lizard Jun 28 '10 at 17:42
  • Yes, the regex is a bit complex, but when you get it from the docs (http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Double.html), it's a lot easier to understand. It also gives you the flexibility to eliminate things like "NaN". – Gabe Jun 28 '10 at 17:47
  • @Gabe: That is a nice feature. I would try to simplify it and catch some of those special cases with a guard clause, but I'm afraid I'm already pretty far into "premature optimization" territory here. – Bill the Lizard Jun 28 '10 at 18:01
  • +1 by the way. And I really need to check what is happening under the hood. I'm curious now. – Pascal Thivent Jun 28 '10 at 19:04
  • @Pascal: Thanks. I tried to find a way to fix this by re-using one Scanner instance, but it doesn't look like it was designed for that. This makes sense, since you're usually I/O bound when you're using a Scanner anyway. – Bill the Lizard Jun 28 '10 at 19:15
  • @Pascal: It looks like `Scanner` builds the expression string and compiles it into a new `Pattern` to match the available input for every invocation of `hasNextDouble`, in addition to doing a lot of house-keeping. Coupled with it calling `Double.valueOf(s)` itself if everything else succeeds, that ends up being a lot of overhead. – Tim Stone Jun 28 '10 at 19:26
  • 4
    I suspect that if you did `Pattern.compile` with the regex and assign it to a static variable, the pattern matcher wouldn't have to compile it every time, and the regex version would go faster. Of course it could do some compiled pattern cacheing under the hood so it might not make a difference. – Gabe Jun 28 '10 at 20:22
  • @Gabe: That seems like the only fair way to make a comparison. I'll post the results in a moment. – Bill the Lizard Jun 28 '10 at 20:48
  • ( A comments about microbenchmarking: You really need to use different runs for timing each approach. Benchmarked code should look like real code - no huge methods (this doesn't seem to be anywhere as important as it used to be, but that's just my impression. `System.nanoTime` will give finer grained timing.) – Tom Hawtin - tackline Sep 13 '10 at 23:11
7

You could create a Scanner(String) and use the hasNextDouble() method. From its javadoc:

Returns true if the next token in this scanner's input can be interpreted as a double value using the nextDouble() method. The scanner does not advance past any input.

For example, this snippet:

List<String> values = Arrays.asList("foo", "1", "2.3", "1f", "0.2d", "3.14");
for (String source : values) {
    Scanner scanner = new Scanner(source);
    System.out.println(String.format("%4s: %s", source, scanner.hasNextDouble()));
}

Would produce the following output:

 foo: false
   1: true
 2.3: true
  1f: false
0.2d: false
3.14: true
Pascal Thivent
  • 562,542
  • 136
  • 1,062
  • 1,124
  • 1
    Using a Scanner is a lot slower than `Double.parseDouble(s)` (orders of magnitude, or I wouldn't bother mentioning it). Maybe you can look at my code below and suggest a way to speed it up. I know creating a new Scanner every time can't be good. – Bill the Lizard Jun 28 '10 at 17:15
  • 1
    @Bill Well, to be honest, the loop was just for the example (and the requirements are unclear). Still, I wasn't expecting such a difference. I'll take a look. – Pascal Thivent Jun 28 '10 at 17:33
  • I was really expecting this to be faster too. The Scanner checks the input before parsing it, which I thought would give it an advantage since no exception is thrown. – Bill the Lizard Jun 28 '10 at 17:47
  • Thanks, for the great answer, very helpful. – Shridutt Kothari Jul 18 '16 at 12:40
6
public boolean isDouble(String value) {
    try {
        Double.parseDouble(value);
        return true;
    } catch (NumberFormatException e) {
        return false;
    }
}
Hendrik Brummermann
  • 8,242
  • 3
  • 31
  • 55
  • That's ok if you know that value cannot be null. Nulls (unlike Integer.parseInt) throw null pointer exceptions, at least in the version of the JDK that I tried. – Yishai Jun 28 '10 at 15:45
  • 6
    Is a null check really that difficult to add? – ILMTitan Jun 28 '10 at 15:56
  • @Yishai: Alternatively, the OP may have meant there was an actual string, not just something that could be either a string or null. It's hard to say, since the question was vague. – David Thornley Jun 28 '10 at 19:28
  • 1
    @ILMTitan, no it is not, if you know about it. The behavior of Integer.parseInt may mislead you into think you don't have to. – Yishai Jun 28 '10 at 19:56
  • @Yishai The OP mentioned 'a value contained in a string', not a variable that could be null. You seem to be just nit-picking. – user207421 Sep 16 '16 at 03:03
5

You can use util class from Apache Commons Lang:

NumberUtils.isNumber(aString);

It's null safe and doesn't require the use of a try-catch block.

Note: for parsing doubles, it works if the decimal separator is a dot .

Edit: isNumber is Deprecated and will be removed from Lang 4.0

Better to use:

NumberUtils.isCreatable(aString);
Łukasz K
  • 562
  • 6
  • 10
3

You could attempt to parse it with Double.parseDouble(String s)

This will return the double if parsing was successful and an an exception if it is not parseable.

So you could wrap the whole thing in a function that contains a try-catch, and return false if you got an exception or true if you got an actual value.

Uri
  • 88,451
  • 51
  • 221
  • 321
3

I would suggest this:

try {
  d = Double.parseDouble(myString);
}
catch (NumberFormatException ex) {
    // Do something smart here...
}
lindiand
  • 31
  • 1
2

We must handle NumberFormatException and null pointer exception to check given String is a numeric or alphanumeric

public static boolean isNumeric(String strNum) {
        try {
           Double.parseDouble(strNum);
        } catch (NumberFormatException | NullPointerException nfe) {
            return false;
        }
        return true;
    }
Rajeev Ranjan
  • 272
  • 2
  • 11
  • 20
1

Others have speculated that you might want to also know that the input is NOT expressed as an integer. Depending on your requirements, this might do the job quick and dirty:

public static void main(String[] args) throws Exception {
    System.out.println(isNonIntegerDouble("12"));  //false
    System.out.println(isNonIntegerDouble("12.1")); //true
    System.out.println(isNonIntegerDouble("12.0")); //true
}

public static boolean isNonIntegerDouble(String in) {
    try {
        Double.parseDouble(in);
    } catch (NumberFormatException nfe) {
        return false;
    }
    try {
        new BigInteger(in);
    } catch (NumberFormatException nfe) {
        return true;
    }
    return false;
}

At this point I feel string matching would be a more suitable choice, however.

Mark Peters
  • 80,126
  • 17
  • 159
  • 190
1

You could use the following regex on the string:

[-+]?[0-9]*\.?[0-9]*

and see if it matches.

KLee1
  • 6,080
  • 4
  • 30
  • 41
  • the problem with this is that it doesn't allow for internationalization. the decimal separator isn't always a `\.` – akf Jun 28 '10 at 16:34
0

I modified Jonas' isInteger() method to come up with the method isDecimal() for my own project and I'm listing the codes below.

May be someone can change add more codes to distinguish between a double and a float.

It should come out pretty speedy and probably better than regex, though I haven't check. The only flaw is it misbehaves in case of overflow condition, etc.

Note that I reference Bill's post at What's the best way to check to see if a String represents an integer in Java?

    public boolean isDecimal(String str) {
        if (str == null) {
            return false;
        }
        int length = str.length();
        if (length == 1 ) {
            return false;
        }
        int i = 0;
        if (str.charAt(0) == '-') {
            if (length < 3) {
                return false;
            }
            i = 1;
        }
        int numOfDot = 0;
        for (; i < length; i++) {
            char c = str.charAt(i);
            if (c == '.')
                numOfDot++;
            else if (c == '/')
                return false;
            else if (c < '.' || c > '9') {
                return false;
            }
        }
        if (numOfDot != 1 )
            return false;

        return true;
    }
Community
  • 1
  • 1
mk7
  • 150
  • 1
  • 9
0

If you could find a way to isolate the number from the string, maybe by using split method. And lets say num[1] = 25; then you could do something like this to check if its a double.

boolean isDouble;
if(num[1].contains(".")){
isDouble = true;
}
else{
isDouble = false;
}
-3
    public boolean isDouble( String input )  
    {  
       try  
       {  
          Double.parseDouble( input );  
          return true;  
       }  
       catch( Exception e)  
       {  
         return false;  
      }  
  }  
Robby Pond
  • 73,164
  • 16
  • 126
  • 119