How do I check if a string represents a number (float or int)?
Asked Answered
G

41

1978

How do I check if a string represents a numeric value in Python?

def is_number(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

The above works, but it seems clunky.


If what you are testing comes from user input, it is still a string even if it represents an int or a float. See How can I read inputs as numbers? for converting the input, and Asking the user for input until they give a valid response for ensuring that the input represents an int or float (or other requirements) before proceeding.

Grays answered 9/12, 2008 at 20:3 Comment(21)
What's wrong with what your current solution? It's short, fast and readable.Medallist
And you don't just have to return True or False. You can return the value suitably modified instead - for example you could use this to put non-numbers in quotes.Muckworm
Wouldn't it better to return the result of float(s) in the case of a successful conversion? You still have the check for success (result is False) and you actually HAVE the conversion, which you are likely to want anyway.Iorio
Thruston - I see your point, but then the check is less trivial.Iorio
Even though this question is older, I just wanted to say that this is an elegant way which is documented as EAFP. So probably the best solution for this kind of problem.Harriet
This seems OK, unless you are validating inputs from a user as locale.atoi("0,1,00") evaluates to 100....Messiaen
Very useful function. In addition if you want to allow space, such as 1e + 2, etc. then this function needs modification.Alethiaaletta
If don't know whether the the input value is a string, you can also catch TypeErrorNatality
pythoncentral.io/…, would help.Ancalin
Return the result of float(s), on success, or None, on fail. Then you get the True/False behavior, as well as being able to directly use the result.Winni
related: Extract float/double valueMaynord
Don't return the result of float(s) or None on fail. if you then use it as x = float('0.00'); if x: use_float(x); you've now got a bug in your code. Truthy values are the reason these functions raise an exception rather than returning None in the first place. A better solution is just to avoid the utility function and surround the call to float in a try catch when you want to use it.Alic
This is part of the problem tackled in the post https://mcmap.net/q/41509/-adding-numbers-in-a-string/…Dichroite
Warning: the snippet tells that any object that automatically casts to float is a number, and this could be misleading. As example, a simple bool results to be a number, because float(True) returns a valid 1.0.Tour
not slow. int(s) or float(s) is likely nearly as fast as what the python interpreter uses for such things and may even have some C code under it. In [17]: time_it(int, '333', count=1) Out[17]: 4.0531158447265625e-06 Pretty darn fast.Manhour
You probably want to get rid of the ValueError part, otherwise this will raise an exception when it gets a list / dict for inputTiemannite
@Colonel Panic it's also slowEnphytotic
The slow part is NOT the float(a). The slow part is the exception throw/catch. As every good programmer knows: NEVER USE EXCEPTIONS FOR NORMAL CODE PATH. The OP did not specify, so I think we ought to assume non-numeric is "normal" input to this function. Is it common practice in python to use exceptions as part of the normal code flow? @SamanthaAtkinsChao
Why not follow lazy checking pattern ? assume that the object is a float and attempt the operation and catch an exception, log it and then cast it to float and reattempt.December
Clunky or not, this is the best answer and I have upvoted it. Yet there's another one with more upvotes (from @Zoomulator) and it is a wrong answer, It is not what the question asks! Well, it's not a mystery. It's just how the human mind works! 😀Gussy
My code to test isfloat is as follow: def is_float(s): if s.isdigit(): return False try: float(s) return True except Exception as e: return FalseMasao
C
780

Which, not only is ugly and slow

I'd dispute both.

A regex or other string parsing method would be uglier and slower.

I'm not sure that anything much could be faster than the above. It calls the function and returns. Try/Catch doesn't introduce much overhead because the most common exception is caught without an extensive search of stack frames.

The issue is that any numeric conversion function has two kinds of results

  • A number, if the number is valid
  • A status code (e.g., via errno) or exception to show that no valid number could be parsed.

C (as an example) hacks around this a number of ways. Python lays it out clearly and explicitly.

I think your code for doing this is perfect.

Connective answered 9/12, 2008 at 20:30 Comment(12)
I don't think that the code is perfect (but I think it's very close): it is more usual to put only the part being "tested" in the try clause, so I would put the return True in an else clause of the try. One of the reasons is that with the code in the question, if I had to review it, I would have to check that the second statement in the try clause cannot raise a ValueError: granted, this does not require too much time or brain power, but why use any when none is needed?Praise
The answer seems compelling, but makes me wonder why it's not provided out-of-the-box... I'll copy this and use it in any case.Seduce
How awful. How about if I don't care what the number is just that it's a number (which is what brought me here)? Instead of a 1-line IsNumeric() I either end up with a try/catch or another wrapping a try/catch. UghSuh
@Suh I do not get your point. Name your function, which does the checking IsNumeric and use that function. Thats the idea of using functions - having one-liners.Buttons
@Nils My point is that it's such an obvious, simple operation available in every other high-level language I've used, that its absence feels like a glaring omission. Then again, this isn't the place for a protracted discussion, so let's agree to disagree.Suh
@Suh - "[Checking if a string is a number is] available in every other high-level language" - I'll agree with you on most of the sentence that I extracted this quote from, but I disagree with the quoted text. Is one available in Java (and if so, since which JDK version?) Last I checked (admittedly several years ago), there wasn't.Whiten
@Whiten The key bit is where I said "That I've used"... Without meaning to start a war, I avoid Java whenever possible. The fact that it's lacking an IsNumeric() surprises me not at all.Suh
@Suh - So I'm curious, what all languages have you used that come with that function out of the box? We've covered that Python and Java don't have it, and if Java doesn't have it, we can rule out C, C++, and probably C# too. Objective-C doesn't have it. There's not many popular high level languages left... does Ruby have it? Does PHP have it? Perl? At this point I think I've named all the popular/major high level languages...Whiten
@Whiten PHP, Javascript and VB.Net off the top of my head. T-SQL has ISNUMERIC() if you'd classify that as a language. C# has Double.TryParse() which will return a boolean although it can be a little woolier (can optionally allow whitespace, thousand separators, etc). Anyway, we're hijacking up this comment thread so I'll come back and delete my comments from today in a few hoursSuh
@Suh - Ah, I neglected JS. SQL is such a narrowly focused language that I wouldn't compare it to Python or any other high level general purpose programming languages. And I have to confess I'm not familiar with PHP, VB, or C#. Your comments are fair enough. I don't see any purpose to deleting them.Whiten
It's not provided 'out of the box' because if is_number(s): x = float(x) else: // fail is the same number of lines of code as the try: x = float(x) catch TypeError: # fail. This utility function is an entirely unecessary abstraction.Alic
But abstraction is the whole point of libraries. Having an 'isNumber' function (in any language) helps a huge amount because you can build it straight into if statements and have far more readable and maintainable code that relying on try - catch blocks. Also, if you need to use the code more than once in more than one class/module, you have then used more lines of code than a built in function would have.Fayum
S
1783

For non-negative (unsigned) integers only, use isdigit():

>>> a = "03523"
>>> a.isdigit()
True
>>> b = "963spam"
>>> b.isdigit()
False

Documentation for isdigit(): Python2, Python3

For Python 2 Unicode strings: isnumeric().

Squeaky answered 9/12, 2008 at 20:15 Comment(18)
That's a negative on negatives as wellSchexnayder
Fails with exponentials too: '1e3'.isdigit() --> FalseTrickster
While Number != Digit, people who are looking for ways to test if a string contains an integer may very well stumble across this question, and the isDigit approach may very well be perfectly suitable for their application.Roldan
Even if this answer doesn't solve the OP's question, in order to solve the issue with floats, what about doing something like a.isdigit() or a.replace('.','').isdigit()?Ambages
@AdamParkin: isdigit() and int() have different opinions about what is an integer e.g., for the Unicode character u'\u00b9': u'¹'.isdigit() is True but int(u'¹') raises ValueError.Maynord
The name isdigit is misleading from the start, since digit means [0-9]Herbie
+1: isdigit() may not be what be what the OP was looking for, but it is exactly what I wanted. It may not be the case that this answer and method doesn't cover all types of numbers, but it is still highly relevant, contrary to the arguments about its accuracy. While "Number != Digit," digit is still a subset of number, particularly numbers that are positive, non-negative, and use base 1-10. Further, this method is particularly useful and brief for cases where you want to inspect whether a string is a numeric ID or not, which often falls into the subset of numbers that I just described.Piliform
@JustinJohnson: have you read my previous comment? You probably want isdecimal() instead.Maynord
@J.F.Sebastian You are right; isdecimal() is the correct method to use in the scenario that I described.Piliform
+1, I was looking for this solution based on my google query, so anyone saying this answer is irrelevant wasn't thinking about anybody except the OP. Since stack overflow has so many answers that show up in google, this is absolutely relevant to those who are looking for this type of solution(like if you need to remove any non-digits from a phone number: phone = ''.join([n for n in someString if n.isdigit()]))Liberati
How does this have 800+ upvotes when it's totally wrong? The fact that there are comments on here that are like "+1, this doesn't answer the question but helps me anyway" blows my mind. -1.Kilimanjaro
@DuckPuncher: for phone numbers, you need isdecimal() instead of isdigit() here (if someString may contain non-ascii characters).Maynord
This bombs on negative numbers, which are numbers, which the OP asked about (OP never specified positive).Stephenstephenie
As others have said, this is a good method, but totally not suitable for the question. Otherwise do people want to mark my answer "chicken" as correct. It is what I had for dinner last night, and is therefore true and correct, just irrelevant to the question asked.On a serious note, yes this method is a good method, but it is completely incorrect for the question asked, which specifically references floats, and should therefore not be upvoted.Marciemarcile
@Jason9987 digit is one of 10 symbols [0-9]. So, maths definitions won't work here anyway.Leilani
You could split on '.' and check if both halves are is_digit() to check a float.Cephalad
I found that using underscores in the string works when passes through int but isdigit returns false.Azeria
This is NOT WHAT THE QUESTION ASKS! How comes this wrong answer got 1,700+ upvotes (up to now)??Gussy
C
780

Which, not only is ugly and slow

I'd dispute both.

A regex or other string parsing method would be uglier and slower.

I'm not sure that anything much could be faster than the above. It calls the function and returns. Try/Catch doesn't introduce much overhead because the most common exception is caught without an extensive search of stack frames.

The issue is that any numeric conversion function has two kinds of results

  • A number, if the number is valid
  • A status code (e.g., via errno) or exception to show that no valid number could be parsed.

C (as an example) hacks around this a number of ways. Python lays it out clearly and explicitly.

I think your code for doing this is perfect.

Connective answered 9/12, 2008 at 20:30 Comment(12)
I don't think that the code is perfect (but I think it's very close): it is more usual to put only the part being "tested" in the try clause, so I would put the return True in an else clause of the try. One of the reasons is that with the code in the question, if I had to review it, I would have to check that the second statement in the try clause cannot raise a ValueError: granted, this does not require too much time or brain power, but why use any when none is needed?Praise
The answer seems compelling, but makes me wonder why it's not provided out-of-the-box... I'll copy this and use it in any case.Seduce
How awful. How about if I don't care what the number is just that it's a number (which is what brought me here)? Instead of a 1-line IsNumeric() I either end up with a try/catch or another wrapping a try/catch. UghSuh
@Suh I do not get your point. Name your function, which does the checking IsNumeric and use that function. Thats the idea of using functions - having one-liners.Buttons
@Nils My point is that it's such an obvious, simple operation available in every other high-level language I've used, that its absence feels like a glaring omission. Then again, this isn't the place for a protracted discussion, so let's agree to disagree.Suh
@Suh - "[Checking if a string is a number is] available in every other high-level language" - I'll agree with you on most of the sentence that I extracted this quote from, but I disagree with the quoted text. Is one available in Java (and if so, since which JDK version?) Last I checked (admittedly several years ago), there wasn't.Whiten
@Whiten The key bit is where I said "That I've used"... Without meaning to start a war, I avoid Java whenever possible. The fact that it's lacking an IsNumeric() surprises me not at all.Suh
@Suh - So I'm curious, what all languages have you used that come with that function out of the box? We've covered that Python and Java don't have it, and if Java doesn't have it, we can rule out C, C++, and probably C# too. Objective-C doesn't have it. There's not many popular high level languages left... does Ruby have it? Does PHP have it? Perl? At this point I think I've named all the popular/major high level languages...Whiten
@Whiten PHP, Javascript and VB.Net off the top of my head. T-SQL has ISNUMERIC() if you'd classify that as a language. C# has Double.TryParse() which will return a boolean although it can be a little woolier (can optionally allow whitespace, thousand separators, etc). Anyway, we're hijacking up this comment thread so I'll come back and delete my comments from today in a few hoursSuh
@Suh - Ah, I neglected JS. SQL is such a narrowly focused language that I wouldn't compare it to Python or any other high level general purpose programming languages. And I have to confess I'm not familiar with PHP, VB, or C#. Your comments are fair enough. I don't see any purpose to deleting them.Whiten
It's not provided 'out of the box' because if is_number(s): x = float(x) else: // fail is the same number of lines of code as the try: x = float(x) catch TypeError: # fail. This utility function is an entirely unecessary abstraction.Alic
But abstraction is the whole point of libraries. Having an 'isNumber' function (in any language) helps a huge amount because you can build it straight into if statements and have far more readable and maintainable code that relying on try - catch blocks. Also, if you need to use the code more than once in more than one class/module, you have then used more lines of code than a built in function would have.Fayum
P
334

TL;DR The best solution is s.replace('.','',1).isdigit()

I did some benchmarks comparing the different approaches

def is_number_tryexcept(s):
    """ Returns True if string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False
       
import re    
def is_number_regex(s):
    """ Returns True if string is a number. """
    if re.match("^\d+?\.\d+?$", s) is None:
        return s.isdigit()
    return True


def is_number_repl_isdigit(s):
    """ Returns True if string is a number. """
    return s.replace('.','',1).isdigit()

If the string is not a number, the except-block is quite slow. But more importantly, the try-except method is the only approach that handles scientific notations correctly.

funcs = [
          is_number_tryexcept, 
          is_number_regex,
          is_number_repl_isdigit
          ]

a_float = '.1234'

print('Float notation ".1234" is not supported by:')
for f in funcs:
    if not f(a_float):
        print('\t -', f.__name__)

Float notation ".1234" is not supported by:

  • is_number_regex

    scientific1 = '1.000000e+50' scientific2 = '1e50'

    print('Scientific notation "1.000000e+50" is not supported by:') for f in funcs: if not f(scientific1): print('\t -', f.name)

    print('Scientific notation "1e50" is not supported by:') for f in funcs: if not f(scientific2): print('\t -', f.name)

Scientific notation "1.000000e+50" is not supported by:

  • is_number_regex
  • is_number_repl_isdigit
    Scientific notation "1e50" is not supported by:
  • is_number_regex
  • is_number_repl_isdigit

EDIT: The benchmark results

import timeit

test_cases = ['1.12345', '1.12.345', 'abc12345', '12345']
times_n = {f.__name__:[] for f in funcs}

for t in test_cases:
    for f in funcs:
        f = f.__name__
        times_n[f].append(min(timeit.Timer('%s(t)' %f, 
                      'from __main__ import %s, t' %f)
                              .repeat(repeat=3, number=1000000)))

where the following functions were tested

from re import match as re_match
from re import compile as re_compile

def is_number_tryexcept(s):
    """ Returns True if string is a number. """
    try:
        float(s)
        return True
    except ValueError:
        return False

def is_number_regex(s):
    """ Returns True if string is a number. """
    if re_match("^\d+?\.\d+?$", s) is None:
        return s.isdigit()
    return True


comp = re_compile("^\d+?\.\d+?$")    

def compiled_regex(s):
    """ Returns True if string is a number. """
    if comp.match(s) is None:
        return s.isdigit()
    return True


def is_number_repl_isdigit(s):
    """ Returns True if string is a number. """
    return s.replace('.','',1).isdigit()

enter image description here

Phyllis answered 13/5, 2014 at 19:28 Comment(15)
for nice charts +1. I saw benchmark and saw graph, all the TL;DR thing became clear and intuitive.Glassco
I agree with @JCChuks: the graph helps a lot to get all the TL;DR quickly. But I think a TL;DR (like : TL;DR : the best solution is s.replace('.','',1).isdigit()) should appear at the beginning of this anwser. In any case it should be the accepted one. Thanks!Cytochemistry
The TLDR is misleading and disingenuous. Being "best" doesn't correlate with any performance benchmark. For example, I usually value readability much more than micro-optimizations, so benchmarks have almost no weight in determining a best solution for my context. TLDR would be more accurate of it stated: "best if ranked by execution time from a small set of arbitrary benchmarks"Heartsick
fair enough, but in the TLDR, I also didn't say that it's based on the benchmarks. To me, it seems also to be the most readable solutionPhyllis
Note that adding .lstrip('-') for negative numbers makes the func is_number_repl_isdigit 1.5x longer to runAmadis
Can you include fastnumbers in the graph (see https://mcmap.net/q/41233/-how-do-i-check-if-a-string-represents-a-number-float-or-int for how to use it in this application)?Cedeno
This method does not handle negative numbers (dashes). I would advocate to just use the float method as it's less prone to mistakes and will work every time.Aport
What's important to note, is that even on the assumption there can't be a dash, the replace-isdigit method is only faster for non-numbers (False outcome), while try-except method is faster for numbers (True outcome). If most of your input is valid input, you're better off with the try-except solution!Actinomycosis
Doesn't work on exponential notation like '1.5e-9' or on negatives.Angell
Nice, did something comparable, without the nice charts, but then for thousands of iterations. When I ran way more cases, the try/catch became a bit more expensive on negative cases than the regex, but when used directly in an if (if test_trycatch_function(x): ... else: ...), it took double the time of the regex. My knowledge of python goes not that deep, so I don't understand why just running or assigning it to a variable that is not further used, I don't know. Maybe some optimizer completely skipping it?Auroraauroral
Brilliant, aside from the obvious caveats of false negatives for exponentiation and negatives – which you can trivially correct by just chaining s.replace() calls. For example, s.replace('.','',1).replace('e-','',1).replace('e','',1).isdigit() handles exponentiation. To then handle negatives, just left-strip the first character if a dash. For example, s.lstrip('-').replace('.','',1).replace('e-','',1).replace('e','',1).isdigit(). Yes, I have exhaustively tested that one-liner and can confirm it behaves as expected.Eliaeliades
You should be tolerant to white space and accept sign, so comp = re_compile("^\s*[+-]?\d+?\.\d+?\s*$").Bigg
Not only this is a good and a detailed answer, but this approach also makes it compatible with Cythonized pandas string methods. +1.Macaroon
@CecilCurry, did your exhaustive testing include 5e6e-7 or similar? I anticipate a false positive for strings including both e- and e.Fit
Aside from the countless other issues that were pointed out with correctness, some (many) cultures use . to represent a thousands separator and , to represent a decimal point. The time (on the order of microseconds) needed to verify input that will be typed by a human, using a keyboard, in response to a prompt in a terminal is absolutely and utterly inconsequential, so calling a solution "best" based on performance here is ridiculous. This answer is terrible.Outgoings
K
83

There is one exception that you may want to take into account: the string 'NaN'

If you want is_number to return FALSE for 'NaN' this code will not work as Python converts it to its representation of a number that is not a number (talk about identity issues):

>>> float('NaN')
nan

Otherwise, I should actually thank you for the piece of code I now use extensively. :)

G.

Kelt answered 1/9, 2010 at 14:6 Comment(4)
Actually, NaN might be a good value to return (rather than False) if the text passed is not in fact a representation of a number. Checking for it is kind of a pain (Python's float type really needs a method for it) but you can use it in calculations without producing an error, and only need to check the result.Ideational
Another exception is the string 'inf'. Either inf or NaN can also be prefixed with a + or - and still be accepted.Holleyholli
If you want to return False for a NaN and Inf, change line to x = float(s); return (x == x) and (x - 1 != x). This should return True for all floats except Inf and NaNCoprolalia
x-1 == x is true for large floats smaller than inf. From Python 3.2 you can use math.isfinite to test for numbers that are neither NaN nor infinite, or check both math.isnan and math.isinf prior to that.Untouchable
W
67

how about this:

'3.14'.replace('.','',1).isdigit()

which will return true only if there is one or no '.' in the string of digits.

'3.14.5'.replace('.','',1).isdigit()

will return false

edit: just saw another comment ... adding a .replace(badstuff,'',maxnum_badstuff) for other cases can be done. if you are passing salt and not arbitrary condiments (ref:xkcd#974) this will do fine :P

Wafer answered 25/5, 2012 at 22:22 Comment(5)
This doesn't however account for negative numbers.Disarmament
Or numbers with exponents like 1.234e56 (which might also be written as +1.234E+56 and several more variants).Cubit
re.match(r'^[+-]*(0[xbo])?[0-9A-Fa-f]*\.?[0-9A-Fa-f]*(E[+-]*[0-9A-Fa-f]+)$', 'str') should do a better job of determining a number (but not all, I'm not claiming that). I don't recommend using this, far better to use the Questioner's original code.Cantal
if you dont like this solution, read this before downvoting!Fingering
man this is the smartest solution I ever seen in this website!, nicely done man!Borak
I
49

Updated after Alfe pointed out you don't need to check for float separately as complex handles both:

def is_number(s):
    try:
        complex(s) # for int, long, float and complex
    except ValueError:
        return False

    return True

Previously said: Is some rare cases you might also need to check for complex numbers (e.g. 1+2i), which can not be represented by a float:

def is_number(s):
    try:
        float(s) # for int, long and float
    except ValueError:
        try:
            complex(s) # for complex
        except ValueError:
            return False

    return True
Ironworker answered 9/12, 2008 at 20:3 Comment(5)
I disagree. That's VERY unlikely in normal use, and you would be better building an is_complex_number() call for when you are using them, rather than burden a call with extra operation for a 0.0001% chance of misoperation.Iorio
You can strip the float() stuff completely and just check for the complex() call to succeed. Everything parsed by float() can be parsed by complex().Cubit
This function will return Pandas's NaNs and Inf values as numeric values.Jubilant
complex('(01989)') will return (1989+0j). But float('(01989)') will fail. So I think using complex is not good idea.Moorehead
Yikes. Bizarre that complex() accepts (- and )-delimited syntax – presumably to account for the composite vector addition in the imaginary plane underlying complex numbers, but still. As @Moorehead suggests, using complex() here invites false positives. Don't do this in production code. Honestly, s.lstrip('-').replace('.','',1).replace('e-','',1).replace('e','',1).isdigit() remains the optimal solution for most use cases.Eliaeliades
W
45

Which, not only is ugly and slow, seems clunky.

It may take some getting used to, but this is the pythonic way of doing it. As has been already pointed out, the alternatives are worse. But there is one other advantage of doing things this way: polymorphism.

The central idea behind duck typing is that "if it walks and talks like a duck, then it's a duck." What if you decide that you need to subclass string so that you can change how you determine if something can be converted into a float? Or what if you decide to test some other object entirely? You can do these things without having to change the above code.

Other languages solve these problems by using interfaces. I'll save the analysis of which solution is better for another thread. The point, though, is that python is decidedly on the duck typing side of the equation, and you're probably going to have to get used to syntax like this if you plan on doing much programming in Python (but that doesn't mean you have to like it of course).

One other thing you might want to take into consideration: Python is pretty fast in throwing and catching exceptions compared to a lot of other languages (30x faster than .Net for instance). Heck, the language itself even throws exceptions to communicate non-exceptional, normal program conditions (every time you use a for loop). Thus, I wouldn't worry too much about the performance aspects of this code until you notice a significant problem.

Worldly answered 11/12, 2008 at 4:56 Comment(3)
Another common place where Python uses exceptions for basic functions is in hasattr() which is just a getattr() call wrapped in a try/except. Still, exception handling is slower than normal flow control, so using it for something that is going to be true most of the time can result in a performance penalty.Ideational
It seems that if you want a one-liner, you're SOLSuh
Also pythonic is the idea that it's "better to ask forgiveness than permission", regarding the impact of having cheap exceptions.Riva
P
35

This answer provides step by step guide having function with examples to find the string is:

  • Positive integer
  • Positive/negative - integer/float
  • How to discard "NaN" (not a number) strings while checking for number?

Check if string is positive integer

You may use str.isdigit() to check whether given string is positive integer.

Sample Results:

# For digit
>>> '1'.isdigit()
True
>>> '1'.isalpha()
False

Check for string as positive/negative - integer/float

str.isdigit() returns False if the string is a negative number or a float number. For example:

# returns `False` for float
>>> '123.3'.isdigit()
False
# returns `False` for negative number
>>> '-123'.isdigit()
False

If you want to also check for the negative integers and float, then you may write a custom function to check for it as:

def is_number(n):
    try:
        float(n)   # Type-casting the string to `float`.
                   # If string is not a valid `float`, 
                   # it'll raise `ValueError` exception
    except ValueError:
        return False
    return True

Sample Run:

>>> is_number('123')    # positive integer number
True

>>> is_number('123.4')  # positive float number
True
 
>>> is_number('-123')   # negative integer number
True

>>> is_number('-123.4') # negative `float` number
True

>>> is_number('abc')    # `False` for "some random" string
False

Discard "NaN" (not a number) strings while checking for number

The above functions will return True for the "NAN" (Not a number) string because for Python it is valid float representing it is not a number. For example:

>>> is_number('NaN')
True

In order to check whether the number is "NaN", you may use math.isnan() as:

>>> import math
>>> nan_num = float('nan')

>>> math.isnan(nan_num)
True

Or if you don't want to import additional library to check this, then you may simply check it via comparing it with itself using ==. Python returns False when nan float is compared with itself. For example:

# `nan_num` variable is taken from above example
>>> nan_num == nan_num
False

Hence, above function is_number can be updated to return False for "NaN" as:

def is_number(n):
    is_number = True
    try:
        num = float(n)
        # check for "nan" floats
        is_number = num == num   # or use `math.isnan(num)`
    except ValueError:
        is_number = False
    return is_number

Sample Run:

>>> is_number('Nan')   # not a number "Nan" string
False

>>> is_number('nan')   # not a number string "nan" with all lower cased
False

>>> is_number('123')   # positive integer
True

>>> is_number('-123')  # negative integer
True

>>> is_number('-1.12') # negative `float`
True

>>> is_number('abc')   # "some random" string
False

PS: Each operation for each check depending on the type of number comes with additional overhead. Choose the version of is_number function which fits your requirement.

Pantograph answered 11/2, 2018 at 8:34 Comment(1)
This should be at the top because it adequately addresses the ambiguity of asking about "a number", and shows corresponding solutions for multiple interpretations.Outgoings
T
34

For int use this:

>>> "1221323".isdigit()
True

But for float we need some tricks ;-). Every float number has one point...

>>> "12.34".isdigit()
False
>>> "12.34".replace('.','',1).isdigit()
True
>>> "12.3.4".replace('.','',1).isdigit()
False

Also for negative numbers just add lstrip():

>>> '-12'.lstrip('-')
'12'

And now we get a universal way:

>>> '-12.34'.lstrip('-').replace('.','',1).isdigit()
True
>>> '.-234'.lstrip('-').replace('.','',1).isdigit()
False
Tyrolienne answered 8/9, 2015 at 8:42 Comment(3)
Doesn't handle things like 1.234e56 and similar. Also, I'd be interested how you'd find out that 99999999999999999999e99999999999999999999 is not a number. Trying to parse it finds out quickly.Cubit
This runs ~30% faster than the accepted solution on a list of 50m strings, and 150% faster on a list of 5k strings. 👏Antimere
Note that >>> '--1234'.lstrip('-').replace('.','',1).isdigit() returns true; perhaps not what's expectedElroy
C
18

For strings of non-numbers, try: except: is actually slower than regular expressions. For strings of valid numbers, regex is slower. So, the appropriate method depends on your input.

If you find that you are in a performance bind, you can use a new third-party module called fastnumbers that provides a function called isfloat. Full disclosure, I am the author. I have included its results in the timings below.


from __future__ import print_function
import timeit

prep_base = '''\
x = 'invalid'
y = '5402'
z = '4.754e3'
'''

prep_try_method = '''\
def is_number_try(val):
    try:
        float(val)
        return True
    except ValueError:
        return False

'''

prep_re_method = '''\
import re
float_match = re.compile(r'[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?$').match
def is_number_re(val):
    return bool(float_match(val))

'''

fn_method = '''\
from fastnumbers import isfloat

'''

print('Try with non-number strings', timeit.timeit('is_number_try(x)',
    prep_base + prep_try_method), 'seconds')
print('Try with integer strings', timeit.timeit('is_number_try(y)',
    prep_base + prep_try_method), 'seconds')
print('Try with float strings', timeit.timeit('is_number_try(z)',
    prep_base + prep_try_method), 'seconds')
print()
print('Regex with non-number strings', timeit.timeit('is_number_re(x)',
    prep_base + prep_re_method), 'seconds')
print('Regex with integer strings', timeit.timeit('is_number_re(y)',
    prep_base + prep_re_method), 'seconds')
print('Regex with float strings', timeit.timeit('is_number_re(z)',
    prep_base + prep_re_method), 'seconds')
print()
print('fastnumbers with non-number strings', timeit.timeit('isfloat(x)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print('fastnumbers with integer strings', timeit.timeit('isfloat(y)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print('fastnumbers with float strings', timeit.timeit('isfloat(z)',
    prep_base + 'from fastnumbers import isfloat'), 'seconds')
print()

Try with non-number strings 2.39108395576 seconds
Try with integer strings 0.375686168671 seconds
Try with float strings 0.369210958481 seconds

Regex with non-number strings 0.748660802841 seconds
Regex with integer strings 1.02021503448 seconds
Regex with float strings 1.08564686775 seconds

fastnumbers with non-number strings 0.174362897873 seconds
fastnumbers with integer strings 0.179651021957 seconds
fastnumbers with float strings 0.20222902298 seconds

As you can see

  • try: except: was fast for numeric input but very slow for an invalid input
  • regex is very efficient when the input is invalid
  • fastnumbers wins in both cases
Cedeno answered 14/8, 2014 at 3:34 Comment(6)
I stand corrected :-} it just didn't look like it was doing this. Maybe using names like prep_code_basis and prep_code_re_method would have prevented my mistake.Cubit
Do you mind explaining how your module works, at least for the isfloat function?Sommersommers
@SolomonUcko Here is a link to the source code for the string checking part: github.com/SethMMorton/fastnumbers/blob/v1.0.0/src/…. Basically, it walks across each character in the string in order and validates that it follows a pattern for a valid float. If the input is already a number, it just uses the fast PyFloat_Check.Cedeno
Tested against the best alternatives in this thread I confirm this solution is by far the fastest. The second fastest method is str(s).strip('-').replace('.','',1).isdigit() which is approximately 10x slower!Amadis
Note that timeit.timeit runs the statement 1 million times. I was confused why these numbers seemed so slow.Boric
This solution is great and should be nearer the top, honestly. It does require a new library, but it is easy to use and behaves in an intuitive manner. Was exactly what I was looking for. Thanks @CedenoOrange
L
16

Just Mimic C#

In C# there are two different functions that handle parsing of scalar values:

  • Float.Parse()
  • Float.TryParse()

float.parse():

def parse(string):
    try:
        return float(string)
    except Exception:
        throw TypeError

Note: If you're wondering why I changed the exception to a TypeError, here's the documentation.

float.try_parse():

def try_parse(string, fail=None):
    try:
        return float(string)
    except Exception:
        return fail;

Note: You don't want to return the boolean 'False' because that's still a value type. None is better because it indicates failure. Of course, if you want something different you can change the fail parameter to whatever you want.

To extend float to include the 'parse()' and 'try_parse()' you'll need to monkeypatch the 'float' class to add these methods.

If you want respect pre-existing functions the code should be something like:

def monkey_patch():
    if(!hasattr(float, 'parse')):
        float.parse = parse
    if(!hasattr(float, 'try_parse')):
        float.try_parse = try_parse

SideNote: I personally prefer to call it Monkey Punching because it feels like I'm abusing the language when I do this but YMMV.

Usage:

float.parse('giggity') // throws TypeException
float.parse('54.3') // returns the scalar value 54.3
float.tryParse('twank') // returns None
float.tryParse('32.2') // returns the scalar value 32.2

And the great Sage Pythonas said to the Holy See Sharpisus, "Anything you can do I can do better; I can do anything better than you."

Laverne answered 18/2, 2012 at 1:35 Comment(8)
I have been coding in mostly JS lately and didn't actually test this so there may be some minor errors. If you see any, feel free to correct my mistakes.Laverne
To add support for complex numbers see the answer by @Matthew Wilcoxson. https://mcmap.net/q/41233/-how-do-i-check-if-a-string-represents-a-number-float-or-int.Laverne
Using ! instead of not might be a minor error, but you definitely can't assign attributes to the built-in float in CPython.Stimson
downvoted for catching all exceptions indiscriminately and for using a keyword that doesn't exist "throw"Duncandunce
I'm the person who flagged "Doesn't answer the question".Sectionalism
@Sectionalism You have more rep than I do, so I hope you know what you're doing, but I don't think that's an appropriate use of the flag. Just downvote if you don't like this answer. But it's still a valid attempt at an answer.Parry
@Parry technically it looks like a valid answer with all the formatting and the bold text, but if you look closely, it just copies the question and moves the goal post to a different idiom. And I found better answers below it so it's just taking up space. At least that's my opinion.Sectionalism
@Sectionalism Well, OK, I've read it careful and I now agree with you. Unfortunately, I've already voted "Looks OK" in my review yesterday.Parry
C
15

I know this is particularly old but I would add an answer I believe covers the information missing from the highest voted answer that could be very valuable to any who find this:

For each of the following methods connect them with a count if you need any input to be accepted. (Assuming we are using vocal definitions of integers rather than 0-255, etc.)

x.isdigit() works well for checking if x is an integer.

x.replace('-','').isdigit() works well for checking if x is a negative.(Check - in first position)

x.replace('.','').isdigit() works well for checking if x is a decimal.

x.replace(':','').isdigit() works well for checking if x is a ratio.

x.replace('/','',1).isdigit() works well for checking if x is a fraction.

Conjecture answered 5/1, 2016 at 15:21 Comment(3)
Though for fractions, you probably need to do x.replace('/','',1).isdigit() or otherwise dates such as 4/7/2017 would be misinterpreted as numbers.Pennoncel
For the best ways to chain the conditions: https://mcmap.net/q/41511/-best-way-to-replace-multiple-characters-in-a-string/5922329Wireless
Similar to Ethan Chen's comment, we'd only want to replace the first decimal/period character, otherwise version numbers such as 0.2.5 would be considered valid floats. So, x.replace('.','',1).isdigit() rather than x.replace('.','').isdigit(), and similarly, a more delicate check for things that aren't negative numbers such as ranges like 5-9 ("5 to 9") in contrast with -59 (the actual negative 59).Terceira
T
12

Casting to float and catching ValueError is probably the fastest way, since float() is specifically meant for just that. Anything else that requires string parsing (regex, etc) will likely be slower due to the fact that it's not tuned for this operation. My $0.02.

Tuff answered 9/12, 2008 at 20:31 Comment(3)
Your "2e-2" dollars are a float too (an additional argument for using float :)Supersession
@Supersession NEVER use a float to represent a monetary value.Atabrine
@Luke: I totally agree with you, although I never suggested using floats to represent monetary values; I just said that monetary values can be represented as floats :)Supersession
E
12

You can use Unicode strings, they have a method to do just what you want:

>>> s = u"345"
>>> s.isnumeric()
True

Or:

>>> s = "345"
>>> u = unicode(s)
>>> u.isnumeric()
True

http://www.tutorialspoint.com/python/string_isnumeric.htm

http://docs.python.org/2/howto/unicode.html

Exine answered 4/3, 2013 at 16:12 Comment(2)
for non-negative ints it is ok ;-)Rabi
s.isdecimal() checks if s string is a non-negative integer. s.isnumeric() includes characters that int() rejects.Maynord
R
10

I wanted to see which method is fastest. Overall the best and most consistent results were given by the check_replace function. The fastest results were given by the check_exception function, but only if there was no exception fired - meaning its code is the most efficient, but the overhead of throwing an exception is quite large.

Please note that checking for a successful cast is the only method which is accurate, for example, this works with check_exception but the other two test functions will return False for a valid float:

huge_number = float('1e+100')

Here is the benchmark code:

import time, re, random, string

ITERATIONS = 10000000

class Timer:    
    def __enter__(self):
        self.start = time.clock()
        return self
    def __exit__(self, *args):
        self.end = time.clock()
        self.interval = self.end - self.start

def check_regexp(x):
    return re.compile("^\d*\.?\d*$").match(x) is not None

def check_replace(x):
    return x.replace('.','',1).isdigit()

def check_exception(s):
    try:
        float(s)
        return True
    except ValueError:
        return False

to_check = [check_regexp, check_replace, check_exception]

print('preparing data...')
good_numbers = [
    str(random.random() / random.random()) 
    for x in range(ITERATIONS)]

bad_numbers = ['.' + x for x in good_numbers]

strings = [
    ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(random.randint(1,10)))
    for x in range(ITERATIONS)]

print('running test...')
for func in to_check:
    with Timer() as t:
        for x in good_numbers:
            res = func(x)
    print('%s with good floats: %s' % (func.__name__, t.interval))
    with Timer() as t:
        for x in bad_numbers:
            res = func(x)
    print('%s with bad floats: %s' % (func.__name__, t.interval))
    with Timer() as t:
        for x in strings:
            res = func(x)
    print('%s with strings: %s' % (func.__name__, t.interval))

Here are the results with Python 2.7.10 on a 2017 MacBook Pro 13:

check_regexp with good floats: 12.688639
check_regexp with bad floats: 11.624862
check_regexp with strings: 11.349414
check_replace with good floats: 4.419841
check_replace with bad floats: 4.294909
check_replace with strings: 4.086358
check_exception with good floats: 3.276668
check_exception with bad floats: 13.843092
check_exception with strings: 15.786169

Here are the results with Python 3.6.5 on a 2017 MacBook Pro 13:

check_regexp with good floats: 13.472906000000009
check_regexp with bad floats: 12.977665000000016
check_regexp with strings: 12.417542999999995
check_replace with good floats: 6.011045999999993
check_replace with bad floats: 4.849356
check_replace with strings: 4.282754000000011
check_exception with good floats: 6.039081999999979
check_exception with bad floats: 9.322753000000006
check_exception with strings: 9.952595000000002

Here are the results with PyPy 2.7.13 on a 2017 MacBook Pro 13:

check_regexp with good floats: 2.693217
check_regexp with bad floats: 2.744819
check_regexp with strings: 2.532414
check_replace with good floats: 0.604367
check_replace with bad floats: 0.538169
check_replace with strings: 0.598664
check_exception with good floats: 1.944103
check_exception with bad floats: 2.449182
check_exception with strings: 2.200056
Ridgway answered 16/1, 2013 at 6:9 Comment(2)
"Please note that checking for a successful cast is the only method which is accurate" <- this isn't actually true. I've run your test using the regexp in my answer above, and it actually runs faster than the regexp. I'll add the results to my answer above.Cason
Incidentally, as an amusing point, your bad numbers creator can actually create some legal numbers, though it would be quite rare. :)Cason
F
10

The input may be as follows:

a="50" b=50 c=50.1 d="50.1"


1-General input:

The input of this function can be everything!

Finds whether the given variable is numeric. Numeric strings consist of optional sign, any number of digits, optional decimal part and optional exponential part. Thus +0123.45e6 is a valid numeric value. Hexadecimal (e.g. 0xf4c3b00c) and binary (e.g. 0b10100111001) notation is not allowed.

is_numeric function

import ast
import numbers              
def is_numeric(obj):
    if isinstance(obj, numbers.Number):
        return True
    elif isinstance(obj, str):
        nodes = list(ast.walk(ast.parse(obj)))[1:]
        if not isinstance(nodes[0], ast.Expr):
            return False
        if not isinstance(nodes[-1], ast.Num):
            return False
        nodes = nodes[1:-1]
        for i in range(len(nodes)):
            #if used + or - in digit :
            if i % 2 == 0:
                if not isinstance(nodes[i], ast.UnaryOp):
                    return False
            else:
                if not isinstance(nodes[i], (ast.USub, ast.UAdd)):
                    return False
        return True
    else:
        return False

test:

>>> is_numeric("54")
True
>>> is_numeric("54.545")
True
>>> is_numeric("0x45")
True

is_float function

Finds whether the given variable is float. float strings consist of optional sign, any number of digits, ...

import ast

def is_float(obj):
    if isinstance(obj, float):
        return True
    if isinstance(obj, int):
        return False
    elif isinstance(obj, str):
        nodes = list(ast.walk(ast.parse(obj)))[1:]
        if not isinstance(nodes[0], ast.Expr):
            return False
        if not isinstance(nodes[-1], ast.Num):
            return False
        if not isinstance(nodes[-1].n, float):
            return False
        nodes = nodes[1:-1]
        for i in range(len(nodes)):
            if i % 2 == 0:
                if not isinstance(nodes[i], ast.UnaryOp):
                    return False
            else:
                if not isinstance(nodes[i], (ast.USub, ast.UAdd)):
                    return False
        return True
    else:
        return False

test:

>>> is_float("5.4")
True
>>> is_float("5")
False
>>> is_float(5)
False
>>> is_float("5")
False
>>> is_float("+5.4")
True

what is ast?


2- If you are confident that the variable content is String:

use str.isdigit() method

>>> a=454
>>> a.isdigit()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute 'isdigit'
>>> a="454"
>>> a.isdigit()
True

3-Numerical input:

detect int value:

>>> isinstance("54", int)
False
>>> isinstance(54, int)
True
>>> 

detect float:

>>> isinstance("45.1", float)
False
>>> isinstance(45.1, float)
True
Fabliau answered 6/10, 2018 at 7:23 Comment(3)
what is "ast"?Sector
An insight on the typesGorgonian
This fails under a test of is_numeric("String 1") Wrapped the method in try/except and works.Lighterage
W
10

In a most general case for a float, one would like to take care of integers and decimals. Let's take the string "1.1" as an example.

I would try one of the following:

1.> isnumeric()

word = "1.1"

"".join(word.split(".")).isnumeric()
>>> True

2.> isdigit()

word = "1.1"

"".join(word.split(".")).isdigit()
>>> True

3.> isdecimal()

word = "1.1"

"".join(word.split(".")).isdecimal()
>>> True

Speed:

► All the aforementioned methods have similar speeds.

%timeit "".join(word.split(".")).isnumeric()
>>> 257 ns ± 12 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit "".join(word.split(".")).isdigit()
>>> 252 ns ± 11 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit "".join(word.split(".")).isdecimal()
>>> 244 ns ± 7.17 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Wellwisher answered 6/12, 2020 at 3:31 Comment(3)
For all of these examples, you're checking if 11 is a number, not 1.1Tamper
I find this the most elegant solution although in theory you are not testing the float "value". What a shame on this piece of Python's str impl, so counterituitive.Aucoin
You need to test some more inputs. Try 1.1.1, which is not a valid number.Fit
D
9

So to put it all together, checking for Nan, infinity and complex numbers (it would seem they are specified with j, not i, i.e. 1+2j) it results in:

def is_number(s):
    try:
        n=str(float(s))
        if n == "nan" or n=="inf" or n=="-inf" : return False
    except ValueError:
        try:
            complex(s) # for complex
        except ValueError:
            return False
    return True
Dobson answered 23/3, 2012 at 16:10 Comment(1)
So far the best answer. ThanksCelsacelsius
C
7

I think your solution is fine, but there is a correct regexp implementation.

There does seem to be a lot of regexp hate towards these answers which I think is unjustified, regexps can be reasonably clean and correct and fast. It really depends on what you're trying to do. The original question was how can you "check if a string can be represented as a number (float)" (as per your title). Presumably you would want to use the numeric/float value once you've checked that it's valid, in which case your try/except makes a lot of sense. But if, for some reason, you just want to validate that a string is a number then a regex also works fine, but it's hard to get correct. I think most of the regex answers so far, for example, do not properly parse strings without an integer part (such as ".7") which is a float as far as python is concerned. And that's slightly tricky to check for in a single regex where the fractional portion is not required. I've included two regex to show this.

It does raise the interesting question as to what a "number" is. Do you include "inf" which is valid as a float in python? Or do you include numbers that are "numbers" but maybe can't be represented in python (such as numbers that are larger than the float max).

There's also ambiguities in how you parse numbers. For example, what about "--20"? Is this a "number"? Is this a legal way to represent "20"? Python will let you do "var = --20" and set it to 20 (though really this is because it treats it as an expression), but float("--20") does not work.

Anyways, without more info, here's a regex that I believe covers all the ints and floats as python parses them.

# Doesn't properly handle floats missing the integer part, such as ".7"
SIMPLE_FLOAT_REGEXP = re.compile(r'^[-+]?[0-9]+\.?[0-9]+([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56"      # sign (-)
                            #     integer (12)
                            #           mantissa (34)
                            #                    exponent (E+56)

# Should handle all floats
FLOAT_REGEXP = re.compile(r'^[-+]?([0-9]+|[0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?$')
# Example "-12.34E+56"      # sign (-)
                            #     integer (12)
                            #           OR
                            #             int/mantissa (12.34)
                            #                            exponent (E+56)

def is_float(str):
  return True if FLOAT_REGEXP.match(str) else False

Some example test values:

True  <- +42
True  <- +42.42
False <- +42.42.22
True  <- +42.42e22
True  <- +42.42E-22
False <- +42.42e-22.8
True  <- .42
False <- 42nope

Running the benchmarking code in @ron-reiter's answer shows that this regex is actually faster than the normal regex and is much faster at handling bad values than the exception, which makes some sense. Results:

check_regexp with good floats: 18.001921
check_regexp with bad floats: 17.861423
check_regexp with strings: 17.558862
check_correct_regexp with good floats: 11.04428
check_correct_regexp with bad floats: 8.71211
check_correct_regexp with strings: 8.144161
check_replace with good floats: 6.020597
check_replace with bad floats: 5.343049
check_replace with strings: 5.091642
check_exception with good floats: 5.201605
check_exception with bad floats: 23.921864
check_exception with strings: 23.755481
Cason answered 10/5, 2019 at 21:15 Comment(1)
Hope that's right - would love to hear about any counter examples. :)Cason
B
7

str.isnumeric()

Return True if all characters in the string are numeric characters, and there is at least one character, False otherwise. Numeric characters include digit characters, and all characters that have the Unicode numeric value property, e.g. U+2155, VULGAR FRACTION ONE FIFTH. Formally, numeric characters are those with the property value Numeric_Type=Digit, Numeric_Type=Decimal or Numeric_Type=Numeric.

str.isdecimal()

Return True if all characters in the string are decimal characters and there is at least one character, False otherwise. Decimal characters are those that can be used to form numbers in base 10, e.g. U+0660, ARABIC-INDIC DIGIT ZERO. Formally a decimal character is a character in the Unicode General Category “Nd”.

Both available for string types from Python 3.0.

Backwoodsman answered 15/4, 2020 at 21:48 Comment(0)
B
5

I needed to determine if a string cast into basic types (float,int,str,bool). After not finding anything on the internet I created this:

def str_to_type (s):
    """ Get possible cast type for a string

    Parameters
    ----------
    s : string

    Returns
    -------
    float,int,str,bool : type
        Depending on what it can be cast to

    """    
    try:                
        f = float(s)        
        if "." not in s:
            return int
        return float
    except ValueError:
        value = s.upper()
        if value == "TRUE" or value == "FALSE":
            return bool
        return type(s)

Example

str_to_type("true") # bool
str_to_type("6.0") # float
str_to_type("6") # int
str_to_type("6abc") # str
str_to_type(u"6abc") # unicode       

You can capture the type and use it

s = "6.0"
type_ = str_to_type(s) # float
f = type_(s) 
Boatload answered 3/7, 2014 at 17:12 Comment(3)
Good answer, but not fully general: str_to_type("123e-4") returns int while float("123e-4") returns 0.0123. Also, str_to_type("NaN") returns int.Parry
Changing if "." not in s: to if "." not in s and "e" not in s and "N" not in s: fixes the cases I show here, but it makes the code clunkier and I'm not sure it will catch all the cases.Parry
My preferred solution, and a safer one, is to use nested try/except statements: wrap i = int(s); return int into a first try/except, then put f = float(s); return float inside a second try/except, all the the except block of the first try/except. Like the structure of user1508746's answer, with different return values.Parry
S
4

I did some speed test. Lets say that if the string is likely to be a number the try/except strategy is the fastest possible.If the string is not likely to be a number and you are interested in Integer check, it worths to do some test (isdigit plus heading '-'). If you are interested to check float number, you have to use the try/except code whitout escape.

Sassaby answered 12/10, 2010 at 7:43 Comment(0)
H
3

RyanN suggests

If you want to return False for a NaN and Inf, change line to x = float(s); return (x == x) and (x - 1 != x). This should return True for all floats except Inf and NaN

But this doesn't quite work, because for sufficiently large floats, x-1 == x returns true. For example, 2.0**54 - 1 == 2.0**54

Hormonal answered 29/7, 2013 at 14:8 Comment(0)
A
3

I was working on a problem that led me to this thread, namely how to convert a collection of data to strings and numbers in the most intuitive way. I realized after reading the original code that what I needed was different in two ways:

1 - I wanted an integer result if the string represented an integer

2 - I wanted a number or a string result to stick into a data structure

so I adapted the original code to produce this derivative:

def string_or_number(s):
    try:
        z = int(s)
        return z
    except ValueError:
        try:
            z = float(s)
            return z
        except ValueError:
            return s
Actor answered 9/11, 2014 at 14:6 Comment(0)
S
2
import re
def is_number(num):
    pattern = re.compile(r'^[-+]?[-0-9]\d*\.\d*|[-+]?\.?[0-9]\d*$')
    result = pattern.match(num)
    if result:
        return True
    else:
        return False


​>>>: is_number('1')
True

>>>: is_number('111')
True

>>>: is_number('11.1')
True

>>>: is_number('-11.1')
True

>>>: is_number('inf')
False

>>>: is_number('-inf')
False
Surface answered 2/8, 2018 at 11:6 Comment(1)
Do you not consider 1e6 to represent a number?Psychodiagnostics
V
2

This code handles the exponents, floats, and integers, wihtout using regex.

return True if str1.lstrip('-').replace('.','',1).isdigit() or float(str1) else False
Viccora answered 16/12, 2018 at 7:12 Comment(1)
Error if string is encounteredUndercast
P
1

Here's my simple way of doing it. Let's say that I'm looping through some strings and I want to add them to an array if they turn out to be numbers.

try:
    myvar.append( float(string_to_check) )
except:
    continue

Replace the myvar.apppend with whatever operation you want to do with the string if it turns out to be a number. The idea is to try to use a float() operation and use the returned error to determine whether or not the string is a number.

Parathyroid answered 16/7, 2009 at 17:45 Comment(1)
You should move the append part of that function into an else statement to avoid accidentally triggering the exception should there be something wrong with the array.Cleanlimbed
M
1

You can generalize the exception technique in a useful way by returning more useful values than True and False. For example this function puts quotes round strings but leaves numbers alone. Which is just what I needed for a quick and dirty filter to make some variable definitions for R.

import sys

def fix_quotes(s):
    try:
        float(s)
        return s
    except ValueError:
        return '"{0}"'.format(s)

for line in sys.stdin:
    input = line.split()
    print input[0], '<- c(', ','.join(fix_quotes(c) for c in input[1:]), ')'
Muckworm answered 24/5, 2013 at 21:36 Comment(0)
F
1

I also used the function you mentioned, but soon I notice that strings as "Nan", "Inf" and it's variation are considered as number. So I propose you improved version of your function, that will return false on those type of input and will not fail "1e3" variants:

def is_float(text):
    try:
        float(text)
        # check for nan/infinity etc.
        if text.isalpha():
            return False
        return True
    except ValueError:
        return False
Football answered 15/10, 2016 at 21:11 Comment(0)
M
1

User helper function:

def if_ok(fn, string):
  try:
    return fn(string)
  except Exception as e:
    return None

then

if_ok(int, my_str) or if_ok(float, my_str) or if_ok(complex, my_str)
is_number = lambda s: any([if_ok(fn, s) for fn in (int, float, complex)])
Manhour answered 15/8, 2019 at 22:18 Comment(1)
Looks interesting, but please explain your solution and how to use it, with some input and output.Bony
K
1
def is_float(s):
    if s is None:
        return False

    if len(s) == 0:
        return False

    digits_count = 0
    dots_count = 0
    signs_count = 0

    for c in s:
        if '0' <= c <= '9':
            digits_count += 1
        elif c == '.':
            dots_count += 1
        elif c == '-' or c == '+':
            signs_count += 1
        else:
            return False

    if digits_count == 0:
        return False

    if dots_count > 1:
        return False

    if signs_count > 1:
        return False

    return True
Kuhlmann answered 3/4, 2020 at 14:39 Comment(0)
L
1

I know I'm late to the party, but figured out a solution which wasn't here: This solution follows the EAFP principle in Python

def get_number_from_string(value):
    try:
        int_value = int(value)
        return int_value

    except ValueError:
        return float(value)

Explanation:

If the value in the string is a float and I first try to parse it as an int, it will throw a ValueError. So, I catch that error and parse the value as float and return.

Latimore answered 14/8, 2021 at 6:24 Comment(2)
Wouldn't this fail again in the except block if value were a string like "1.f"?Medullary
@sinekonata, it is assumed in the question that the number is present as a string.Latimore
S
1

One fast and simple option is to check the data type:

def is_number(value):
    return type(value) in [int, float]

Or if you want to test if the values os a string are numeric:

def isNumber (value):
    return True if type(value) in [int, float] else str(value).replace('.','',1).isdigit()

tests:

>>> isNumber(1)
True

>>> isNumber(1/3)
True

>>> isNumber(1.3)
True

>>> isNumber('1.3')
True

>>> isNumber('s1.3')
False
Skindive answered 5/1, 2022 at 18:20 Comment(2)
what about e for exponent?Sleety
yeap, @johnktejik That can be a problem. This function will return false to strings like '1.54-e8' and '0X45'. For this kind of number you can use regex to correct the issue.Skindive
L
1

There are already good answers in this post. I wanted to give a slightly different perspective.

Instead of searching for a digit, number or float we could do a negative search for an alphabet. i.e. we could ask the program to look if it is not alphabet.

## Check whether it is not alpha rather than checking if it is digit
print(not "-1.2345".isalpha())
print(not "-1.2345e-10".isalpha())

It will work well if you are sure that your string is a well formed number (Condition 1 and Condition 2 below). However it will fail if the string is not a well formed number by mistake. In such a case it will return a number match even if the string was not a valid number. To take care of this situation, there are many rule based methods must be there. However at this moment, regex comes to my mind. Below are three cases. Please note regex can be much better since I am not a regex expert. Below there are two lists: one for valid numbers and one for invalid numbers. Valid numbers must be picked up while the invalid numbers must not be.

== Condition 1: String is guranteed to be a valid number but 'inf' is not picked ==

Valid_Numbers = ["1","-1","+1","0.0",".1","1.2345","-1.2345","+1.2345","1.2345e10","1.2345e-10","-1.2345e10","-1.2345E10","-inf"]
Invalid_Numbers = ["1.1.1","++1","--1","-1-1","1.23e10e5","--inf"]

################################ Condition 1: Valid number excludes 'inf' ####################################

Case_1_Positive_Result = list(map(lambda x: not x.isalpha(),Valid_Numbers))
print("The below must all be True")
print(Case_1_Positive_Result)

## This check assumes a valid number. So it fails for the negative cases and wrongly detects string as number
Case_1_Negative_Result = list(map(lambda x: not x.isalpha(),Invalid_Numbers))
print("The below must all be False")
print(Case_1_Negative_Result)
The below must all be True
[True, True, True, True, True, True, True, True, True, True, True, True, True]
The below must all be False
[True, True, True, True, True, True]

== Condition 2: String is guranteed to be a valid number and 'inf' is picked ==

################################ Condition 2: Valid number includes 'inf'  ###################################
Case_2_Positive_Result = list(map(lambda x: x=="inf" or not x.isalpha(),Valid_Numbers+["inf"]))
print("The below must all be True")
print(Case_2_Positive_Result)

## This check assumes a valid number. So it fails for the negative cases and wrongly detects string as number
Case_2_Negative_Result = list(map(lambda x: x=="inf" or not x.isalpha(),Invalid_Numbers+["++inf"]))
print("The below must all be False")
print(Case_2_Negative_Result)
The below must all be True
[True, True, True, True, True, True, True, True, True, True, True, True, True, True]
The below must all be False
[True, True, True, True, True, True, True]

== Condition 3: String is not guranteed to be a valid number ==

import re
CompiledPattern = re.compile(r"([+-]?(inf){1}$)|([+-]?[0-9]*\.?[0-9]*$)|([+-]?[0-9]*\.?[0-9]*[eE]{1}[+-]?[0-9]*$)")
Case_3_Positive_Result = list(map(lambda x: True if CompiledPattern.match(x) else False,Valid_Numbers+["inf"]))
print("The below must all be True")
print(Case_3_Positive_Result)

## This check assumes a valid number. So it fails for the negative cases and wrongly detects string as number
Case_3_Negative_Result = list(map(lambda x: True if CompiledPattern.match(x) else False,Invalid_Numbers+["++inf"]))
print("The below must all be False")
print(Case_3_Negative_Result)
The below must all be True
[True, True, True, True, True, True, True, True, True, True, True, True, True, True]
The below must all be False
[False, False, False, False, False, False, False]
Lory answered 26/11, 2022 at 5:48 Comment(0)
S
0

Try this.

 def is_number(var):
    try:
       if var == int(var):
            return True
    except Exception:
        return False
Skillern answered 30/5, 2015 at 17:12 Comment(2)
Fails to respond with is_number('10')Samarasamarang
@geotheory, what do you mean "fails to respond"?Sommersommers
S
0

Sorry for the Zombie thread post - just wanted to round out the code for completeness...

# is_number() function - Uses re = regex library
# Should handle all normal and complex numbers
# Does not accept trailing spaces. 
# Note: accepts both engineering "j" and math "i" but only the imaginary part "+bi" of a complex number a+bi
# Also accepts inf or NaN
# Thanks to the earlier responders for most the regex fu

import re

ISNUM_REGEXP = re.compile(r'^[-+]?([0-9]+|[0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?[ij]?$')

def is_number(str):
#change order if you have a lot of NaN or inf to parse
    if ISNUM_REGEXP.match(str) or str == "NaN" or str == "inf": 
        return True 
    else:
        return False
# A couple test numbers
# +42.42e-42j
# -42.42E+42i

print('Is it a number?', is_number(input('Gimme any number: ')))

Gimme any number: +42.42e-42j

Is it a number? True

Staffan answered 22/2, 2021 at 3:13 Comment(0)
L
0

For my very simple and very common use-case: is this human written string with keyboard a number?

I read through most answers, and ended up with:

def isNumeric(string):
    result = True
    try:
        x = float(string)
       result = (x == x) and (x - 1 != x)
    except ValueError:
        result = False
    return result

It will return False for (+-)NaN and (+-)inf.

You can check it out here: https://trinket.io/python/ce32c0e54e

Lauralee answered 16/7, 2021 at 14:23 Comment(0)
M
0

My short answer is: may be duplicated one, sorry for that ... def is_float(s): if s.isdigit(): return False try: float(s) return True except Exception as e: return False

Masao answered 12/3, 2023 at 12:21 Comment(0)
N
0
def is_number(x:str):
    x = x.replace(".", "", 1)
    if x.startswith("-"):
        x = x[1:]
    return x.isdigit()

if __name__ == '__main__':
    for si in ["123.456", "-123.456", "123", "-123", "--123", "a123", "123a"]:
        print(si, is_number(si))
Noahnoak answered 12/7, 2023 at 5:43 Comment(0)
R
-2

I have a similar problem. Instead of defining a isNumber function, I want to convert a list of strings to floats, something that in high-level terms would be:

[ float(s) for s in list if isFloat(s)]

It is a given we can not really separate the float(s) from the isFloat(s) functions: these two results should be returned by the same function. Also, if float(s) fails, the whole process fails, instead of just ignoring the faulty element. Plus, "0" is a valid number and should be included in the list. When filtering out bad elements, be certain not to exclude 0.

Therefore, the above comprehension must be modified somehow to:

  • if any element in the list cannot be converted, ignore it and don't throw an exception
  • avoid calling float(s) more than once for each element (one for the conversion, the other for the test)
  • if the converted value is 0, it should still be present in the final list

I propose a solution inspired in the Nullable numerical types of C#. These types are internally represented by a struct that has the numerical value and adds a boolean indicating if the value is valid:

def tryParseFloat(s):
    try:
        return(float(s), True)
    except:
        return(None, False)

tupleList = [tryParseFloat(x) for x in list]
floats = [v for v,b in tupleList if b]
Revision answered 18/3, 2018 at 0:18 Comment(0)
N
-3

use following it handles all cases:-

import re
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.3') 
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.')
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '.3')
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.3sd')
a=re.match('((\d+[\.]\d*$)|(\.)\d+$)' ,  '2.3')
Norward answered 24/2, 2017 at 11:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.