Checking if a string can be converted to float in Python

435,550

Solution 1

I would just use..

try:
    float(element)
except ValueError:
    print "Not a float"

..it's simple, and it works. Note that it will still throw OverflowError if element is e.g. 1<<1024.

Another option would be a regular expression:

import re
if re.match(r'^-?\d+(?:\.\d+)$', element) is None:
    print "Not float"

Solution 2

Python method to check for float:

def is_float(element: Any) -> bool:
    try:
        float(element)
        return True
    except ValueError:
        return False

Always do unit testing. What is, and is not a float may surprise you:

Command to parse                        Is it a float?  Comment
--------------------------------------  --------------- ------------
print(isfloat(""))                      False
print(isfloat("1234567"))               True 
print(isfloat("NaN"))                   True            nan is also float
print(isfloat("NaNananana BATMAN"))     False
print(isfloat("123.456"))               True
print(isfloat("123.E4"))                True
print(isfloat(".1"))                    True
print(isfloat("1,234"))                 False
print(isfloat("NULL"))                  False           case insensitive
print(isfloat(",1"))                    False           
print(isfloat("123.EE4"))               False           
print(isfloat("6.523537535629999e-07")) True
print(isfloat("6e777777"))              True            This is same as Inf
print(isfloat("-iNF"))                  True
print(isfloat("1.797693e+308"))         True
print(isfloat("infinity"))              True
print(isfloat("infinity and BEYOND"))   False
print(isfloat("12.34.56"))              False           Two dots not allowed.
print(isfloat("#56"))                   False
print(isfloat("56%"))                   False
print(isfloat("0E0"))                   True
print(isfloat("x86E0"))                 False
print(isfloat("86-5"))                  False
print(isfloat("True"))                  False           Boolean is not a float.   
print(isfloat(True))                    True            Boolean is a float
print(isfloat("+1e1^5"))                False
print(isfloat("+1e1"))                  True
print(isfloat("+1e1.3"))                False
print(isfloat("+1.3P1"))                False
print(isfloat("-+1"))                   False
print(isfloat("(1)"))                   False           brackets not interpreted

Solution 3

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

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

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

will return false

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

will return false

Solution 4

TL;DR:

  • If your input is mostly strings that can be converted to floats, the try: except: method is the best native Python method.
  • If your input is mostly strings that cannot be converted to floats, regular expressions or the partition method will be better.
  • If you are 1) unsure of your input or need more speed and 2) don't mind and can install a third-party C-extension, fastnumbers works very well.

There is another method available via a third-party module called fastnumbers (disclosure, I am the author); it provides a function called isfloat. I have taken the unittest example outlined by Jacob Gabrielson in this answer, but added the fastnumbers.isfloat method. I should also note that Jacob's example did not do justice to the regex option because most of the time in that example was spent in global lookups because of the dot operator... I have modified that function to give a fairer comparison to try: except:.


def is_float_try(str):
    try:
        float(str)
        return True
    except ValueError:
        return False

import re
_float_regexp = re.compile(r"^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$").match
def is_float_re(str):
    return True if _float_regexp(str) else False

def is_float_partition(element):
    partition=element.partition('.')
    if (partition[0].isdigit() and partition[1]=='.' and partition[2].isdigit()) or (partition[0]=='' and partition[1]=='.' and partition[2].isdigit()) or (partition[0].isdigit() and partition[1]=='.' and partition[2]==''):
        return True
    else:
        return False

from fastnumbers import isfloat


if __name__ == '__main__':
    import unittest
    import timeit

    class ConvertTests(unittest.TestCase):

        def test_re_perf(self):
            print
            print 're sad:', timeit.Timer('ttest.is_float_re("12.2x")', "import ttest").timeit()
            print 're happy:', timeit.Timer('ttest.is_float_re("12.2")', "import ttest").timeit()

        def test_try_perf(self):
            print
            print 'try sad:', timeit.Timer('ttest.is_float_try("12.2x")', "import ttest").timeit()
            print 'try happy:', timeit.Timer('ttest.is_float_try("12.2")', "import ttest").timeit()

        def test_fn_perf(self):
            print
            print 'fn sad:', timeit.Timer('ttest.isfloat("12.2x")', "import ttest").timeit()
            print 'fn happy:', timeit.Timer('ttest.isfloat("12.2")', "import ttest").timeit()


        def test_part_perf(self):
            print
            print 'part sad:', timeit.Timer('ttest.is_float_partition("12.2x")', "import ttest").timeit()
            print 'part happy:', timeit.Timer('ttest.is_float_partition("12.2")', "import ttest").timeit()

    unittest.main()

On my machine, the output is:

fn sad: 0.220988988876
fn happy: 0.212214946747
.
part sad: 1.2219619751
part happy: 0.754667043686
.
re sad: 1.50515985489
re happy: 1.01107215881
.
try sad: 2.40243887901
try happy: 0.425730228424
.
----------------------------------------------------------------------
Ran 4 tests in 7.761s

OK

As you can see, regex is actually not as bad as it originally seemed, and if you have a real need for speed, the fastnumbers method is quite good.

Solution 5

Just for variety here is another method to do it.

>>> all([i.isnumeric() for i in '1.2'.split('.',1)])
True
>>> all([i.isnumeric() for i in '2'.split('.',1)])
True
>>> all([i.isnumeric() for i in '2.f'.split('.',1)])
False

Edit: Im sure it will not hold up to all cases of float though especially when there is an exponent. To solve that it looks like this. This will return True only val is a float and False for int but is probably less performant than regex.

>>> def isfloat(val):
...     return all([ [any([i.isnumeric(), i in ['.','e']]) for i in val],  len(val.split('.')) == 2] )
...
>>> isfloat('1')
False
>>> isfloat('1.2')
True
>>> isfloat('1.2e3')
True
>>> isfloat('12e3')
False
Share:
435,550

Related videos on Youtube

Chris Upchurch
Author by

Chris Upchurch

I am a professor at the University of South Carolina. I'm not a professional programmer, but I do some programming in the course of my research work. I like to use python when I can but I am occasionally forced to use VB.

Updated on April 19, 2022

Comments

  • Chris Upchurch
    Chris Upchurch about 2 years

    I've got some Python code that runs through a list of strings and converts them to integers or floating point numbers if possible. Doing this for integers is pretty easy

    if element.isdigit():
      newelement = int(element)
    

    Floating point numbers are more difficult. Right now I'm using partition('.') to split the string and checking to make sure that one or both sides are digits.

    partition = element.partition('.')
    if (partition[0].isdigit() and partition[1] == '.' and partition[2].isdigit()) 
        or (partition[0] == '' and partition[1] == '.' and partition[2].isdigit()) 
        or (partition[0].isdigit() and partition[1] == '.' and partition[2] == ''):
      newelement = float(element)
    

    This works, but obviously the if statement for that is a bit of a bear. The other solution I considered is to just wrap the conversion in a try/catch block and see if it succeeds, as described in this question.

    Anyone have any other ideas? Opinions on the relative merits of the partition and try/catch approaches?

    • bombs
      bombs almost 3 years
      To be precise, there is no such thing as type-conversion in Python. So the tag type-conversion is misleading, which is a well-defined term in some languages.
    • ggorlen
      ggorlen over 2 years
      @bombs no type convesion? Then what do you call this: print(type("1"));print(type(int("1"))), output: <class 'str'> <class 'int'>? Is this not a type conversion from str to int?
    • bombs
      bombs over 2 years
      @ggorlen Not at all. You are just creating new objects from other objects. You allocate new memory spaces in the heap along the way. Nothing is converted.
    • ggorlen
      ggorlen over 2 years
      I see what you mean, but that seems pedantic to me. If someone says "convert string to float" the intent seems pretty universally clear.
    • Charlie Parker
      Charlie Parker over 2 years
      why did you use .isdigit() and not .isnumeric(): for positive ints?
  • Chris Upchurch
    Chris Upchurch about 15 years
    @S.Lott: Most of the strings this is applied to will turn out to be ints or floats.
  • dustyburwell
    dustyburwell about 15 years
    Is there not a tryfloat(element) or otherwise equivalent function like C#'s float.TryParse(element). Typically excepting is not very performant. Not that this is something that will happen very often, but if it's in a tight loop, it could be an issue.
  • dhara tcrails
    dhara tcrails about 15 years
    Your regex is not optimal. "^\d+\.\d+$" will fail a match at the same speed as above, but will succeed faster. Also, a more correct way would be: "^[+-]?\d(>?\.\d+)?$" However, that still doesn't match numbers like: +1.0e-10
  • Erusa Chan
    Erusa Chan about 15 years
    Except that you forgot to name your function "will_it_float".
  • Patrick B.
    Patrick B. over 9 years
    The second option won't catch nan and exponential expression - such as 2e3.
  • Gewthen
    Gewthen over 8 years
    Regex is much more slow and memory intensive to use and in this case not an option for use in production code.
  • Carlos
    Carlos almost 8 years
    I think the regex is not parsing negative numbers.
  • Mad Physicist
    Mad Physicist over 7 years
    Not optimal but actually pretty clever. Won't handle +/- and exponents.
  • Csaba Toth
    Csaba Toth about 7 years
    Couldn't we start with the if text.isalpha(): check right away?
  • Csaba Toth
    Csaba Toth about 7 years
    BTW I need the same: I don't want accept NaN, Inf and stuff
  • ragardner
    ragardner almost 7 years
    the fast numbers check works so well if you have a majority of strings that can't be converted to floats, really speeds things up, thankyou
  • BareNakedCoder
    BareNakedCoder almost 7 years
    Great answer. Just adding 2 more where float=True: isfloat(" 1.23 ") and isfloat(" \n \t 1.23 \n\t\n"). Useful in web requests; no need to trim white spaces first.
  • Daniil Mashkin
    Daniil Mashkin over 6 years
    In [2]: '123,123'.isdigit() Out[2]: False
  • RandomEli
    RandomEli over 6 years
    It does not work for negative numbers literal, please fix your answer
  • Ébe Isaac
    Ébe Isaac over 5 years
    Could you please state which one is faster?
  • gwideman
    gwideman over 5 years
    The isnumeric function looks like a poor choice, as it returns true on various Unicode characters like fractions. Docs say: "Numeric characters include digit characters, and all characters that have the Unicode numeric value property, e.g. U+2155, VULGAR FRACTION ONE FIFTH"
  • Ohad the Lad
    Ohad the Lad almost 5 years
    '39.1'.isdigit()
  • Mark Moretto
    Mark Moretto over 4 years
    Years late, but this is a nice method. Worked for me using the following in a pandas dataframe: [i for i in df[i].apply(lambda x: str(x).replace('.','').isdigit()).any()]
  • David Heffernan
    David Heffernan over 4 years
    @MarkMoretto You are going to be in for a shock when you learn of the existence of negative numbers
  • lotrus28
    lotrus28 over 4 years
    all([x.isdigit() for x in str(VAR).strip('-').replace(',','.').split('.')]) If you are looking for a more complete implementation.
  • double-beep
    double-beep about 4 years
    While this code may solve the question, including an explanation of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply.
  • madladzen
    madladzen about 4 years
    straight up isnt working for me and is just closing terminal RIP
  • MJohnyJ
    MJohnyJ about 4 years
    Best one-liner for my scenario, where I just need to check for positive floats or numbers. I like.
  • dariober
    dariober over 3 years
    Another addition that may be surpsing: isfloat("1_2_3.4") -> True
  • Erik Wognsen
    Erik Wognsen over 3 years
    Note that float() can also raise "TypeError: float() argument must be a string or a number". It is not enough to check for ValueError.
  • Gaurav Koradiya
    Gaurav Koradiya almost 3 years
    You can also place and operator like this. def is_float(s): result = False if s.count(".") == 1 and s.replace(".", "").isdigit(): result = True return result
  • ggorlen
    ggorlen over 2 years
    Naming a variable str overwrites a builtin function.
  • ggorlen
    ggorlen over 2 years
    What's the point of the (?:)? Isn't r'^-?\d+\.\d+$' the same thing?
  • Levi
    Levi over 2 years
    You likely want to except type errors as well for isfloat(None and isfloat(False) Edit: Removed my code example as the multiline formatting as quite mangled.
  • Ralph
    Ralph over 2 years
    @dariober Why ?
  • étale-cohomology
    étale-cohomology about 2 years
    very nice, elegant
  • Hawkeye Parker
    Hawkeye Parker about 2 years
    am_i_a_number.strip().lstrip('-').replace('.', '', 1).isdigit()