Take the 2-minute tour ×

I know variants of this question have been asked frequently before (see here and here for instance), but this is not an exact duplicate of those.

I would like to check if a String is a number, and if so I would like to store it as a double. There are several ways to do this, but all of them seem inappropriate for my purposes.

One solution would be to use Double.parseDouble(s) or similarly new BigDecimal(s). However, those solutions don't work if there are commas present (so "1,234" would cause an exception). I could of course strip out all commas before using these techniques, but that would seem to pose loads of problems in other locales.

I looked at Apache Commons NumberUtils.isNumber(s), but that suffers from the same comma issue.

I considered NumberFormat or DecimalFormat, but those seemed far too lenient. For instance, "1A" is formatted to "1" instead of indicating that it's not a number. Furthermore, something like "" will be counted as the number 127 instead of indicating that it's not a number.

I feel like my requirements aren't so exotic that I'm the first to do this, but none of the solutions does exactly what I need. I suppose even I don't know exactly what I need (otherwise I could write my own parser), but I know the above solutions do not work for the reasons indicated. Does any solution exist, or do I need to figure out precisely what I need and write my own code for it?

share|improve this question
Does the code really need to provide internationalization support? The locale worry might be unfounded. –  Thomas Langston May 4 '11 at 19:44
@Thomas I don't know if it is unfounded or not; I've been tempted to just pretend like Europe does not exist to simplify things :) –  Michael McGowan May 4 '11 at 19:55
this looks duplicate of stackoverflow.com/questions/4323599/… . Have you checked answers here ? –  YoK Feb 8 '12 at 5:36
@YoK Yes I checked there; that question was referenced by maaron BEFORE the bounty was placed. The answers there suggested NumberFormat when I was explicit as to why that does not work here. –  Michael McGowan Feb 8 '12 at 5:50
I think the problem you face here, is that you do not let people specify where they are from. If you know someone uses , or . to define a decimal, you can just do locale based number parsing and all will be fine. If you insist on having a locale free input field, and then converting it to a format your database expects, you will probably end up validating and parsing the formats yourself. –  Benjamin Udink ten Cate Feb 12 '12 at 9:06

15 Answers 15

up vote 13 down vote accepted

Sounds quite weird, but I would try to follow this answer and use java.util.Scanner.

Scanner scanner = new Scanner(input);
if (scanner.hasNextInt())
else if (scanner.hasNextDouble())
    System.out.println("Not a number");

For inputs such as 1A,, 1,234, 6.02e-23 I get the following output:

Not a number
Not a number

Scanner.useLocale can be used to change to the desired locale.

share|improve this answer
Haven't seen the scanner one before...neat. But, what you have there fails if you pass "1 A" where the scanner word separation will give you multiple tokens. So, you would need to modify your above such that a Number 'n' is set on hasNextInt and hasNextDouble, skip the final else and then have a separate if statement to see if the scanner has any following tokens, ala n!=null && scanner.hasNext(). Both this and the ParsePosition allow leading and trailing whitespace - so that would still be one for op to deal with as appropriate but with these fixes I think you have a pretty good solution. –  philwb Feb 8 '12 at 1:17
This answer would benefit from a mention of useLocale, as well as philwb’s addition. Otherwise, top. –  Konrad Rudolph Feb 10 '12 at 11:54
@KonradRudolph, Scanner.useLocale is already mentioned on the last line of the answer. As far as @philwb's suggestion is concerned, I think it really depends on possible constraints you may have on the input the program receives. For example, if the string is allowed to contain multiple tokens, and space is used to separate them, I think that trimming, splitting, then cycling on the resulting array with the snippet I provided would do. The OP doesn't mention anything about such constraints, just providing some example tokens, so I prefer not to steer my answer in one direction or another. –  Giulio Piancastelli Feb 10 '12 at 12:24
@Giulio So it is. I overlooked that. –  Konrad Rudolph Feb 10 '12 at 13:04

You can specify the Locale that you need:

NumberFormat nf = NumberFormat.getInstance(Locale.GERMAN);
double myNumber = nf.parse(myString).doubleValue();

This should work in your example since German Locale has commas as decimal separator.

share|improve this answer
Regardless of locale, I already explicitly specified that NumberFormat is too lenient because it accepts things like "1A" and "" as numbers. –  Michael McGowan Feb 7 '12 at 13:06

You can use the ParsePosition as a check for complete consumption of the string in a NumberFormat.parse operation. If the string is consumed, then you don't have a "1A" situation. If not, you do and can behave accordingly. See here for a quick outline of the solution and here for the related JDK bug that is closed as wont fix because of the ParsePosition option.

share|improve this answer
While this is interesting, it does not seem to handle scientific notation. For instance, "6.02e-23" is not recognized as a number with this technique. –  Michael McGowan Feb 7 '12 at 17:43
Apparently, it's a limitation of NumberFormat, not of the particular ParsePosition-based technique: "DecimalFormat can be instructed to format and parse scientific notation only via a pattern; there is currently no factory method that creates a scientific notation format." –  Giulio Piancastelli Feb 8 '12 at 0:01
Exactly! DecimalFormat is the way to go. –  RockyMM Feb 10 '12 at 13:16

Unfortunately Double.parseDouble(s) or new BigDecimal(s) seem to be your best options.

You cite localisation concerns, but unfortunately there is no way reliably support all locales w/o specification by the user anyway. It is just impossible.

Sometimes you can reason about the scheme used by looking at whether commas or periods are used first, if both are used, but this isn't always possible, so why even try? Better to have a system which you know works reliably in certain situations than try to rely on one which may work in more situations but can also give bad results...

What does the number 123,456 represent? 123456 or 123.456?

Just strip commas, or spaces, or periods, depending on locale specified by user. Default to stripping spaces and commas. If you want to make it stricter, only strip commas OR spaces, not both, and only before the period if there is one. Also should be pretty easy to check manually if they are spaced properly in threes. In fact a custom parser might be easiest here.

Here is a bit of a proof of concept. It's a bit (very) messy but I reckon it works, and you get the idea anyways :).

public class StrictNumberParser {
  public double parse(String numberString) throws NumberFormatException {
    numberString = numberString.trim();
    char[] numberChars = numberString.toCharArray();

    Character separator = null;
    int separatorCount = 0;
    boolean noMoreSeparators = false;
    for (int index = 1; index < numberChars.length; index++) {
      char character = numberChars[index];

      if (noMoreSeparators || separatorCount < 3) {
        if (character == '.') {
          if (separator != null) {
            throw new NumberFormatException();
          } else {
            noMoreSeparators = true;
        } else if (separator == null && (character == ',' || character == ' ')) {
          if (noMoreSeparators) {
            throw new NumberFormatException();
          separator = new Character(character);
          separatorCount = -1;
        } else if (!Character.isDigit(character)) {
          throw new NumberFormatException();

      } else {
        if (character == '.') {
          noMoreSeparators = true;
        } else if (separator == null) {
          if (Character.isDigit(character)) {
            noMoreSeparators = true;
          } else if (character == ',' || character == ' ') {
            separator = new Character(character);
          } else {
            throw new NumberFormatException();
        } else if (!separator.equals(character)) {
          throw new NumberFormatException();

        separatorCount = 0;

    if (separator != null) {
      if (!noMoreSeparators && separatorCount != 3) {
        throw new NumberFormatException();
      numberString = numberString.replaceAll(separator.toString(), "");

    return Double.parseDouble(numberString);

  public void testParse(String testString) {
    try {
      System.out.println("result: " + parse(testString));
    } catch (NumberFormatException e) {
      System.out.println("Couldn't parse number!");

  public static void main(String[] args) {
    StrictNumberParser p = new StrictNumberParser();
    p.testParse("123 45.6");
    p.testParse("123 4567.8");
    p.testParse("123 4567");
    p.testParse("12 45");
    p.testParse("123 456 45");
    p.testParse("123 456,789");
    p.testParse("123 456 789.52");
    p.testParse("123 456.12");

EDIT: obviously this would need to be extended for recognising scientific notation, but this should be simple enough, especially as you don't have to actually validate anything after the e, you can just let parseDouble fail if it is badly formed.

Also might be a good idea to properly extend NumberFormat with this. have a getSeparator() for parsed numbers and a setSeparator for giving desired output format... This sort of takes care of localisation, but again more work would need to be done to support ',' for decimals...

share|improve this answer

Not sure if it meets all your requirements, but the code found here might point you in the right direction?

From the article:

To summarize, the steps for proper input processing are:

  1. Get an appropriate NumberFormat and define a ParsePosition variable.
  2. Set the ParsePosition index to zero.
  3. Parse the input value with parse(String source, ParsePosition parsePosition).
  4. Perform error operations if the input length and ParsePosition index value don't match or if the parsed Number is null.
  5. Otherwise, the value passed validation.
share|improve this answer
Sounds promising but would you mind summarising the relevant part inside your answer? That way, a stale link won’t destroy its usefulness. –  Konrad Rudolph Feb 10 '12 at 11:56

This is an interesting problem. But perhaps it is a little open-ended? Are you looking specifically to identify base-10 numbers, or hex, or what? I'm assuming base-10. What about currency? Is that important? Or is it just numbers.

In any case, I think that you can use the deficiencies of Number format to your advantage. Since you no that something like "1A", will be interpreted as 1, why not check the result by formatting it and comparing against the original string?

public static boolean isNumber(String s){
        Locale l = Locale.getDefault();
        DecimalFormat df = new DecimalFormat("###.##;-##.##");
        Number n = df.parse(s);
        String sb = df.format(n);
        return sb.equals(s);
    catch(Exception e){
        return false;

What do you think?

share|improve this answer
Unfortunately, in North America that would typically result in (1,024 != 1024), which is a false negative. –  Falkreon Feb 11 '12 at 18:56

This is really interesting, and I think people are trying to overcomplicate it. I would really just break this down by rules:

1) Check for scientific notation (does it match the pattern of being all numbers, commas, periods, -/+ and having an 'e' in it?) -- if so, parse however you want

2) Does it match the regexp for valid numeric characters (0-9 , . - +) (only 1 . - or + allowed) if so, strip out everything that's not a digit and parse appropriately, otherwise fail.

I can't see a shortcut that's going to work here, just take the brute force approach, not everything in programming can be (or needs to be) completely elegant.

share|improve this answer

My understanding is that you want to cover Western/Latin languages while retaining as much strict interpretation as possible. So what I'm doing here is asking DecimalFormatSymbols to tell me what the grouping, decimal, negative, and zero separators are, and swapping them out for symbols Double will recognize.

How does it perform?

In the US, it rejects: "1A", "" and accepts "1.47E-9"

In Germany it still rejects "1A"

It ACCEPTS "1,024.00" but interprets it correctly as 1.024. Likewise, it accepts "" as 127100100100.0

In fact, the German locale correctly identifies and parses "1,47E-9"

Let me know if you have any trouble in a different locale.

import java.util.Locale;
import java.text.DecimalFormatSymbols;

public class StrictNumberFormat {

public static boolean isDouble(String s, Locale l) {
    String clean = convertLocaleCharacters(s,l);

    try {
        return true;
    } catch (NumberFormatException nfe) {
        return false;

public static double doubleValue(String s, Locale l) {
    return Double.valueOf(convertLocaleCharacters(s,l));

public static boolean isDouble(String s) {
    return isDouble(s,Locale.getDefault());

public static double doubleValue(String s) {
    return doubleValue(s,Locale.getDefault());

private static String convertLocaleCharacters(String number, Locale l) {
    DecimalFormatSymbols symbols = new DecimalFormatSymbols(l);
    String grouping = getUnicodeRepresentation( symbols.getGroupingSeparator() );
    String decimal = getUnicodeRepresentation( symbols.getDecimalSeparator() );
    String negative = getUnicodeRepresentation( symbols.getMinusSign() );
    String zero = getUnicodeRepresentation( symbols.getZeroDigit() );

    String clean = number.replaceAll(grouping, "");
    clean = clean.replaceAll(decimal, ".");
    clean = clean.replaceAll(negative, "-");
    clean = clean.replaceAll(zero, "0");

    return clean;

private static String getUnicodeRepresentation(char ch) {
    String unicodeString = Integer.toHexString(ch); //ch implicitly promoted to int
    while(unicodeString.length()<4) unicodeString = "0"+unicodeString;

    return "\\u"+unicodeString;

share|improve this answer

You're best off doing it manually. Figure out what you can accept as a number and disregard everything else:

   import java.lang.NumberFormatException;
   import java.util.regex.Pattern;
   import java.util.regex.Matcher;

   public class ParseDouble {
   public static void main(String[] argv) {

       String line = "$$$|%|#|1A||1,344|95|99.64";

       for (String s : line.split("\\|")) {
           try {
               System.out.println("parsed: " + 

           }catch (NumberFormatException ne) {
   public static double any2double(String input) throws NumberFormatException {

       double out =0d;

       Pattern special         = Pattern.compile("[^a-zA-Z0-9\\.,]+");
       Pattern letters         = Pattern.compile("[a-zA-Z]+");
       Pattern comma           = Pattern.compile(",");
       Pattern allDigits       = Pattern.compile("^[0-9]+$");
       Pattern singleDouble    = Pattern.compile("^[0-9]+\\.[0-9]+$");

       Matcher[] goodCases = new Matcher[]{

       Matcher[] nanCases = new Matcher[]{

       // maybe cases 
       if (comma.matcher(input).find()){
           out = Double.parseDouble( 
           return out;


       for (Matcher m : nanCases) {
           if (m.find()) {
               throw new NumberFormatException("Bad input "+input);

       for (Matcher m : goodCases) {

           if (m.find()) {
               try {
                   out = Double.parseDouble(input);
                   return out;
               } catch (NumberFormatException ne){
       throw new NumberFormatException("Could not parse "+input);
share|improve this answer
This answer is identical to Double.valueOf(input) except that it removes commas. The problem is that it won't consider different grouping separators or minus signs, and if you wanted to start, you'd have to completely rework the regexps - a task I don't wish on my enemies. –  Falkreon Feb 12 '12 at 15:22
line.split("\\|") This hurts my eyes. line.split(Pattern.quote("|")) is a better solution IMO. –  Garbage Feb 13 '12 at 12:16
I appreciate that. Thank you! –  Ярослав Рахматуллин Feb 14 '12 at 10:34

If you set your locale right, built in parseDouble will work with commas. Example is here.

share|improve this answer
Your link refers not to parseDouble but to NumberFormat which, as the OP has stated, doesn’t work properly. –  Konrad Rudolph Feb 10 '12 at 11:51

I think you've got a multi step process to handle here with a custom solution, if you're not willing to accept the results of DecimalFormat or the answers already linked.

1) Identify the decimal and grouping separators. You might need to identify other format symbols (such as scientific notation indicators).


2) Strip out all grouping symbols (or craft a regex, be careful of other symbols you accept such as the decimal if you do). Then strip out the first decimal symbol. Other symbols as needed.

3) Call parse or isNumber.

share|improve this answer
I like this plan. I used it below ^_^ –  Falkreon Feb 12 '12 at 15:33

One of the easy hacks would be to use replaceFirst for String you get and check the new String whether it is a double or not. In case it's a double - convert back (if needed)

share|improve this answer

If you want to convert some string number which is comma separated decimal to double, you could use DecimalSeparator + DecimalFormalSymbols:

final double strToDouble(String str, char separator){
    DecimalFormatSymbols s = new DecimalFormatSymbols();
    DecimalFormat df = new DecimalFormat();

    double num = 0;
        num = ((Double) df.parse(str)).doubleValue();
    }catch(ClassCastException | ParseException ex){
        // if you want, you could add something here to 
        // indicate the string is not double
    return num;

well, lets test it:

    String a = "1.2";
    String b = "2,3";
    String c = "A1";
    String d = "";

    System.out.println("\"" + a + "\" = " + strToDouble(a, ','));
    System.out.println("\"" + a + "\" (with '.' as separator) = " 
            + strToDouble(a, '.'));
    System.out.println("\"" + b + "\" = " + strToDouble(b, ','));
    System.out.println("\"" + c + "\" = " + strToDouble(c, ','));
    System.out.println("\"" + d + "\" = " + strToDouble(d, ','));

if you run the above code, you'll see:

"1.2" = 0.0
"1.2" (with '.' as separator) = 1.2
"2,3" = 2.3
"A1" = 0.0
"" = 0.0
share|improve this answer

This will take a string, count its decimals and commas, remove commas, conserve a valid decimal (note that this is based on US standardization - in order to handle 1.000.000,00 as 1 million this process would have to have the decimal and comma handling switched), determine if the structure is valid, and then return a double. Returns null if the string could not be converted. Edit: Added support for international or US. convertStoD(string,true) for US, convertStoD(string,false) for non US. Comments are now for US version.

public double convertStoD(string s,bool isUS){
 //string s = "some string or number, something dynamic";
 bool isNegative = false;
 if(s.charAt(0)== '-')
  s = s.subString(1);
  isNegative = true;
 string ValidNumberArguements = new string();
   ValidNumberArguements = ",.";
   ValidNumberArguements = ".,";
 int length = s.length;
 int currentCommas = 0;
 int currentDecimals = 0;
  if(s.charAt(i) == ValidNumberArguements.charAt(0))//charAt(0) = ,
  if(s.charAt(i) == ValidNumberArguements.charAt(1))//charAt(1) = .
  if(s.charAt(i).matches("\D"))return null;//remove 1 A
 if(currentDecimals > 1)return null;//remove 1.00.00
 string decimalValue = "";
 if(currentDecimals > 0)
   int index = s.indexOf(ValidNumberArguements.charAt(1));
   decimalValue += s.substring(index);
   s = s.substring(0,index);
   if(decimalValue.indexOf(ValidNumberArguements.charAt(0)) != -1)return null;//remove 1.00,000
 int allowedCommas = (s.length-1) / 3;
 if(currentCommas > allowedCommas)return null;//remove 10,00,000
 String[] NumberParser = s.split(ValidNumberArguements.charAt(0));
 length = NumberParser.length;
 StringBuilder returnString = new StringBuilder();
   if(i == 0)
     if(NumberParser[i].length > 3 && length > 1)return null;//remove 1234,0,000
   if(NumberParser[i].length != 3)return null;//ensure proper 1,000,000
 double answer = Double.parseDouble(returnString);
 if(isNegative)answer *= -1;
 return answer;
share|improve this answer
This answer is interesting. It handles some things mine does not - commas are validated in their proper positions. I'm a little curious about why you used ValidNumberArguements to store ",." and then used the character literal ',' later - it seems like it would be better to put them each in their own place and refer to them consistently. This would also let you pull new values from Locale/DecimalFormatSymbols and get some location awareness in here. –  Falkreon Feb 12 '12 at 15:30
I did that for clarity, just in case it wasn't as obvious. I changed the code to be more consistent, hopefully it is still obvious. The reason for using a string of ValidNumberArguements was for extendability, in case different arguments needed to be used in the future they could be added to the string and then referenced later in the same fashion. –  Travis J Feb 13 '12 at 18:45

This code should handle most inputs, except IP addresses where all groups of digits are in three's (ex: is valid, but not It also doesn't support scientific notation

It will work with most variants of separators (",", "." or space). If more than one separator is detected, the first is assumed to be the thousands separator, with additional checks (validity etc.)

Edit: prevDigit is used for checking that the number uses thousand separators correctly. If there are more than one group of thousands, all but the first one must be in groups of 3. I modified the code to make it clearer so that "3" is not a magic number but a constant.

Edit 2: I don't mind the down votes much, but can someone explain what the problem is?

/* A number using thousand separator must have
   groups of 3 digits, except the first one.
   Numbers following the decimal separator can
   of course be unlimited. */
private final static int GROUP_SIZE=3;

public static boolean isNumber(String input) {
    boolean inThousandSep = false;
    boolean inDecimalSep = false;
    boolean endsWithDigit = false;
    char thousandSep = '\0';
    int prevDigits = 0;

        char c = input.charAt(i);

        switch(c) {
            case ',':
            case '.':
            case ' ':
                endsWithDigit = false;
                    return false;
                else if(inThousandSep) {
                    if(c != thousandSep)
                        inDecimalSep = true;
                    if(prevDigits != GROUP_SIZE)
                        return false; // Invalid use of separator
                else {
                    if(prevDigits > GROUP_SIZE || prevDigits == 0)
                        return false;
                    thousandSep = c;
                    inThousandSep = true;
                prevDigits = 0;

                if(Character.isDigit(c)) {
                    endsWithDigit = true;
                else {
                    return false;
    return endsWithDigit;

Test code:

public static void main(String[] args) {
    System.out.println(isNumber("100"));               // true
    System.out.println(isNumber("100.00"));            // true
    System.out.println(isNumber("1,5"));               // true
    System.out.println(isNumber("1,000,000.00."));     // false
    System.out.println(isNumber("100,00,2"));          // false
    System.out.println(isNumber(""));    // false
    System.out.println(isNumber(""));   // true       
share|improve this answer
What's special about prevDigits being 3? There's nothing magical about IP addresses; I just used that as an example of something containing only digits and decimal points that is nevertheless not a number (whereas some standard techniques would falsely recognize it as a number). –  Michael McGowan Feb 10 '12 at 23:18
To clarify: I said the code doesn't check for IP addresses specifically. The prevDigits being 3 was just an added check to validate the correct usage of thousand separators. As a side effect, it will invalidate many IP addresses. Sorry if that was confusing, but English is not my first language. –  Optimist Feb 10 '12 at 23:43
The biggest problem with this approach is its complete lack of awareness of the current Locale. A good example is that Germany uses comma and period opposite of the way it is in the US. We'd ideally want to translate 1,024 in the US as 1024.0D, and in Germany as 1.024D –  Falkreon Feb 12 '12 at 15:02
(argh... silly edits) this answer also won't cover locales that have a grouping amount other than 3, or a different minus sign or zero, or scientific notation, and there's no way to decode the number once it's recognized. Sorry, I don't want to sound like a hater. You put some good work into this and gave us a big chunk of code and test cases to work with, and that helps a ton. I think heuristic input validation could really work (and potentially be really cool), just not for this question. –  Falkreon Feb 12 '12 at 15:15

Your Answer


