Originally Published: 11 July 2003
One of the frequently asked questions in various .NET developer venues is "how can I check for a numeric value if I am using C#?" The question itself is not precise because it applies equally to other .NET languages, but those using VB.NET, particularly those with a VB or VBScript background, take the IsNumeric function for granted. What they and many new .NET developers don't know is that this function is actually contained in Microsoft's Microsoft.VisualBasic.dll assembly, which can be used by any .NET language. So the comparisons in this article are just as valid for VB.NET programmers as they are for those programming in other languages.
For this benchmark, I compared six different methods of checking a value to see if it is numeric:
The test system is a Dell Inspiron 8500 with a Pentium 4-M 2.2GHz CPU, 1GB DDR PC2100 RAM, and 40GB 5400RPM IDE HD. The application was built and runs on version 1.1 of the .NET Framework, optimized and without debugging information (default Release configuration in VS.NET). The test involves calling each method 100,000 times using the given input, clocking the start and end time, and extracting the elapsed time in seconds. I ran two tests for each method, one using input of a valid decimal number (Figure 1) and one using an input of an invalid string containing both numerals and letters (Figure 2). Please feel free to download the source and run benchmarks on your own machines.
As you can see in Figure 1, the differences between the six methods are not that dramatic when there is a valid input. I did find that the Regular Expression tended to slow down with larger inputs, but overall, given that these numbers were produced by 100,000 tries, the time required for each and the difference between each is very insignificant. The incremental char search has the clear edge of about one tenth of a second.
In Figure 2, we see the trend take a dramatic turn, with both methods using a catch block increasing over tenfold to around nine seconds, which highlights the extra effort involved in catching exceptions. The other methods are almost identical to their counterparts in the previous test, excepting the Regular Expression, which added a good two tenths of a second. And again, the incremental char search wins out in raw performance by about a tenth of a second. You may be thinking that if there were more valid numbers prior to the invalid chars that it would lose the advantage (since it would short circuit later), but I tested into the trillions with about the same performance.
So which method should you use? It depends on the application, and maybe on your choice of language. With regard to the latter, I simply concede that if you are using the VB.NET IDE, it is very easy to just use IsNumeric, and since it is fast and flexible, it may be the option of choice in that environment. But even if you are using the VB.NET IDE, you may want to consider using Double.TryParse.
In any case, keep in mind that for the given regular expression and incremental char comparison implementations, the definition of "IsNumeric" is limited to digits with one optional decimal. If you needed to expand this definition to include, e.g., allowing globalization, allowing signs, allowing scientific notation, etc., you could customize them to meet your definition of what a number is, but I would expect that as you get more flexible with the definition, these methods would start to lose out in raw performance (and simplicity).
Therefore, given the insignificant practical performance difference between the Incremental Char, Double.TryParse, and VisualBasic IsNumeric methods, I would choose either Double.TryParse or VB IsNumeric since their definitions of "IsNumeric" include many common variations not accounted for in the Regular Expression or Incremental Char. Clearly, using one of the Try-Catch methods is a bad idea due to the terrible performance for bad inputs; only use these if you truly do need to handle the exception.
For most applications, the best method will be TryParse because it offers the most flexibility in definition, the ability to return the converted value concurrently with the type check, and it is part of the System library, so no extra references or work is needed to use it, no matter what language you choose. If you find it too cumbersome to use directly (because it offers no overloads), you can always wrap it in a static IsNumeric method that only requires the input to check as a parameter.