Checking if a string can be converted to float in Python [duplicate]
Asked Answered
P

24

302

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?

Propose answered 9/4, 2009 at 21:52 Comment(5)
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.Lavoie
@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?Reglet
@Reglet 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.Lavoie
I see what you mean, but that seems pedantic to me. If someone says "convert string to float" the intent seems pretty universally clear.Reglet
why did you use .isdigit() and not .isnumeric(): for positive ints?Clipboard
P
437

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")
Plain answered 9/4, 2009 at 21:55 Comment(12)
@S.Lott: Most of the strings this is applied to will turn out to be ints or floats.Propose
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.Griseous
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-10Terracotta
Except that you forgot to name your function "will_it_float".Seymourseys
The second option won't catch nan and exponential expression - such as 2e3.Benkley
Regex is much more slow and memory intensive to use and in this case not an option for use in production code.Guria
I think the regex is not parsing negative numbers.Godolphin
Could you please state which one is faster?Stent
straight up isnt working for me and is just closing terminal RIPSnake
Note that float() can also raise "TypeError: float() argument must be a string or a number". It is not enough to check for ValueError.Mana
What's the point of the (?:)? Isn't r'^-?\d+\.\d+$' the same thing?Reglet
It's also notable that the regular expression here will not detect floats in scientific notation, such as '1.67E3'.Pinnate
P
312

Python3 method to check for float:

def is_float(element: any) -> bool:
    #If you expect None to be passed:
    if element is None: 
        return False
    try:
        float(element)
        return True
    except ValueError:
        return False

Python2 version of the above: How do I parse a string to a float or int?

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("1_2_3.4"))               True        123.4, underscores ignored
print(isfloat("NaN"))                   True        nan is also float
print(isfloat("123.456"))               True
print(isfloat("123.E4"))                True
print(isfloat(".1"))                    True
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("1,234"))                 False
print(isfloat("NULL"))                  False       case insensitive
print(isfloat("NaNananana BATMAN"))     False
print(isfloat(",1"))                    False           
print(isfloat("123.EE4"))               False           
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

Sinking exceptions like this is bad, because killing canaries is bad because the float method can fail for reasons other than user input. Do not be using code like this on life critical software. Also python has been changing its contract on what unicode strings can be promoted to float so expect this behavior of this code to change on major version updates.

Paludal answered 5/1, 2014 at 3:56 Comment(5)
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.Misfire
Another addition that may be surpsing: isfloat("1_2_3.4") -> TrueCattycornered
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.Futuristic
@Cattycornered Why ?Osmosis
Should be any, not Any. Else looks solid!Bowes
C
68
'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

Clevie answered 12/7, 2016 at 12:52 Comment(8)
Not optimal but actually pretty clever. Won't handle +/- and exponents.Shipwreck
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()]Stopgap
@MarkMoretto You are going to be in for a shock when you learn of the existence of negative numbersMorphia
Best one-liner for my scenario, where I just need to check for positive floats or numbers. I like.Outlandish
am_i_a_number.strip().lstrip('-').replace('.', '', 1).isdigit()N
@HawkeyeParker lol, nice rebound. +1 for saving face in the comments section lol.Jelene
should be removeprefix('-') instead of lstrip('-'), so this will correctly flag "--5" as not a float.Bullough
float('10e-1') doesn't work for this I fearBasilbasilar
T
16

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.

Tenuis answered 14/8, 2014 at 3:13 Comment(2)
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, thankyouFestive
Naming a variable str overwrites a builtin function.Reglet
Z
7

If you cared about performance (and I'm not suggesting you should), the try-based approach is the clear winner (compared with your partition-based approach or the regexp approach), as long as you don't expect a lot of invalid strings, in which case it's potentially slower (presumably due to the cost of exception handling).

Again, I'm not suggesting you care about performance, just giving you the data in case you're doing this 10 billion times a second, or something. Also, the partition-based code doesn't handle at least one valid string.

$ ./floatstr.py
F..
partition sad: 3.1102449894
partition happy: 2.09208488464
..
re sad: 7.76906108856
re happy: 7.09421992302
..
try sad: 12.1525540352
try happy: 1.44165301323
.
======================================================================
FAIL: test_partition (__main__.ConvertTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./floatstr.py", line 48, in test_partition
    self.failUnless(is_float_partition("20e2"))
AssertionError

----------------------------------------------------------------------
Ran 8 tests in 33.670s

FAILED (failures=1)

Here's the code (Python 2.6, regexp taken from John Gietzen's answer):

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)?$")
def is_float_re(str):
    return re.match(_float_regexp, str)


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 pa\
rtition[2].isdigit()) or (partition[0].isdigit() and partition[1]=='.' and partition[2]==''):
        return True

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

    class ConvertTests(unittest.TestCase):
        def test_re(self):
            self.failUnless(is_float_re("20e2"))

        def test_try(self):
            self.failUnless(is_float_try("20e2"))

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

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

        def test_partition_perf(self):
            print
            print 'partition sad:', timeit.Timer('floatstr.is_float_partition("12.2x")', "import floatstr").timeit()
            print 'partition happy:', timeit.Timer('floatstr.is_float_partition("12.2")', "import floatstr").timeit()

        def test_partition(self):
            self.failUnless(is_float_partition("20e2"))

        def test_partition2(self):
            self.failUnless(is_float_partition(".2"))

        def test_partition3(self):
            self.failIf(is_float_partition("1234x.2"))

    unittest.main()
Zalucki answered 9/4, 2009 at 22:56 Comment(0)
H
6

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
Hypothermia answered 23/5, 2018 at 17:47 Comment(1)
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"Froude
C
6

Simplified version of the function is_digit(str), which suffices in most cases (doesn't consider exponential notation and "NaN" value):

def is_digit(str):
    return str.lstrip('-').replace('.', '').isdigit()
Cittern answered 15/9, 2018 at 18:4 Comment(1)
very nice, elegantCivies
J
4

If you don't need to worry about scientific or other expressions of numbers and are only working with strings that could be numbers with or without a period:

Function

def is_float(s):
    result = False
    if s.count(".") == 1:
        if s.replace(".", "").isdigit():
            result = True
    return result

Lambda version

is_float = lambda x: x.replace('.','',1).isdigit() and "." in x

Example

if is_float(some_string):
    some_string = float(some_string)
elif some_string.isdigit():
    some_string = int(some_string)
else:
    print "Does not convert to int or float."

This way you aren't accidentally converting what should be an int, into a float.

Joost answered 8/10, 2015 at 9:50 Comment(1)
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 resultMediacy
T
3

This regex will check for scientific floating point numbers:

^[-+]?(?:\b[0-9]+(?:\.[0-9]*)?|\.[0-9]+\b)(?:[eE][-+]?[0-9]+\b)?$

However, I believe that your best bet is to use the parser in a try.

Terracotta answered 9/4, 2009 at 22:30 Comment(0)
K
3

It seems many regex given miss one thing or another. This has been working for me so far:

(?i)^\s*[+-]?(?:inf(inity)?|nan|(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?)\s*$

It allows for infinity (or inf) with sign, nan, no digit before the decimal, and leading/trailing spaces (if desired). The ^ and $ are needed to keep from partially matching something like 1.2f-2 as 1.2.

You could use [ed] instead of just e if you need to parse some files where D is used for double-precision scientific notation. You would want to replace it afterward or just replace them before checking since the float() function won't allow it.

Kidney answered 26/2, 2021 at 3:19 Comment(0)
T
2

I used the function already 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 the function, that will return false on those type of input and will not fail "1e3" variants:

def is_float(text):
    # check for nan/infinity etc.
    if text.isalpha():
        return False
    try:
        float(text)
        return True
    except ValueError:
        return False
Troika answered 15/10, 2016 at 21:15 Comment(2)
Couldn't we start with the if text.isalpha(): check right away?Touchback
BTW I need the same: I don't want accept NaN, Inf and stuffTouchback
C
2

You can use the try-except-else clause , this will catch any conversion/ value errors raised when the value passed cannot be converted to a float


  def try_parse_float(item):
      result = None
      try:
        float(item)
      except:
        pass
      else:
        result = float(item)
      return result
Circumspection answered 19/2, 2020 at 13:20 Comment(2)
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.Fedora
it's very bad practice to use catch all exceptionsConstringe
D
2

a simple function that get you the type of number without try and except operation

def number_type(number):
    if number.isdigit():
        return int(number)
    elif number.replace(".","").isdigit():
        return float(number)
    else:
        return(type(number))
Dansby answered 22/3, 2022 at 0:52 Comment(2)
number_type("192.168.0.1") gives ValueError: could not convert string to float: '192.168.0.1'.Mania
only replace the first dot instance: number.replace(".", "", 1).isdigit()Inversion
C
1

I was looking for some similar code, but it looks like using try/excepts is the best way. Here is the code I'm using. It includes a retry function if the input is invalid. I needed to check if the input was greater than 0 and if so convert it to a float.

def cleanInput(question,retry=False): 
    inputValue = input("\n\nOnly positive numbers can be entered, please re-enter the value.\n\n{}".format(question)) if retry else input(question)
    try:
        if float(inputValue) <= 0 : raise ValueError()
        else : return(float(inputValue))
    except ValueError : return(cleanInput(question,retry=True))


willbefloat = cleanInput("Give me the number: ")
Crinkleroot answered 18/3, 2018 at 8:18 Comment(0)
R
1

Try to convert to float. If there is an error, print the ValueError exception.

try:
    x = float('1.23')
    print('val=',x)
    y = float('abc')
    print('val=',y)
except ValueError as err:
    print('floatErr;',err)

Output:

val= 1.23
floatErr: could not convert string to float: 'abc'
Rheims answered 31/12, 2018 at 15:44 Comment(0)
A
1

Passing dictionary as argument it will convert strings which can be converted to float and will leave others

def covertDict_float(data):
        for i in data:
            if data[i].split(".")[0].isdigit():
                try:
                    data[i] = float(data[i])
                except:
                    continue
        return data
Alleviative answered 13/9, 2019 at 11:15 Comment(0)
E
1

I tried some of the above simple options, using a try test around converting to a float, and found that there is a problem in most of the replies.

Simple test (along the lines of above answers):

entry = ttk.Entry(self, validate='key')
entry['validatecommand'] = (entry.register(_test_num), '%P')

def _test_num(P):
    try: 
        float(P)
        return True
    except ValueError:
        return False

The problem comes when:

  • You enter '-' to start a negative number:

You are then trying float('-') which fails

  • You enter a number, but then try to delete all the digits

You are then trying float('') which likewise also fails

The quick solution I had is:

def _test_num(P):
    if P == '' or P == '-': return True
    try: 
        float(P)
        return True
    except ValueError:
        return False
Eburnation answered 3/4, 2020 at 22:9 Comment(0)
C
0

I found a way that could also work. need to verify this. first time putting something here.

def isfloat(a_str):
    try:
        x=float(a_str)
        if x%1 == 0:
            return False
        elif x%1 != 0: #an else also do
            return True
    except Exception as error:
            return False
Condign answered 30/4, 2021 at 4:43 Comment(0)
D
0

This works like a charm:

[dict([a,int(x) if isinstance(x, str)
 and x.isnumeric() else float(x) if isinstance(x, str)
 and x.replace('.', '', 1).isdigit() else x] for a, x in json_data.items())][0]
Delineator answered 30/4, 2021 at 16:56 Comment(0)
R
0

I've written my own functions. Instead of float(value), I use floatN() or floatZ(). which return None or 0.0 if the value can't be cast as a float. I keep them in a module I've called safeCasts.

def floatN(value):
    try:
        if value is not None:
            fvalue = float(value)
        else:
            fvalue = None
    except ValueError:
        fvalue = None

    return fvalue


def floatZ(value):
    try:
        if value is not None:
            fvalue = float(value)
        else:
            fvalue = 0.0
    except ValueError:
        fvalue = 0.0

    return fvalue

In other modules I import them

from safeCasts import floatN, floatZ

then use floatN(value) or floatZ(value) instead of float(). Obviously, you can use this technique for any cast function you need.

Renell answered 25/10, 2021 at 8:43 Comment(0)
A
0

It's a simple, yet interesting question. Solution presented below works fine for me:

import re

val = "25,000.93$"

regex = r"\D"

splitted = re.split(regex, val)
splitted = list(filter(str.isdecimal, splitted))

if splitted:
    if len(splitted) > 1:
        splitted.insert(-1, ".")

    try:
        f = float("".join(splitted))
        print(f, "is float.")
        
    except ValueError:
        print("Not a float.")
        
else:
    print("Not a float.")

Important note: this solution is based on assumption that the last value in splitted contains decimal places.

Aerator answered 30/1, 2022 at 22:23 Comment(0)
N
0

You can create a function isfloat(), and use in place of isdigit() for both integers and floats, but not strings as you expect.

a = raw_input('How much is 1 share in that company? \n')

def isfloat(num):
    try:
        float(num)
        return True
    except:
        return False
       
while not isfloat(a):
    print("You need to write a number!\n")
    a = raw_input('How much is 1 share in that company? \n')
Netherlands answered 18/4, 2022 at 13:8 Comment(0)
H
-2

We can use regex as: import re if re.match('[0-9]*.?[0-9]+', <your_string>): print("Its a float/int") else: print("Its something alien") let me explain the regex in english,

  • * -> 0 or more occurence
  • + -> 1 or more occurence
  • ? -> 0/1 occurence

now, lets convert

  • '[0-9]* -> let there be 0 or more occurence of digits in between 0-9
  • \.? -> followed by a 0 or one '.'(if you need to check if it can be int/float else we can also use instead of ?, use {1})
  • [0-9]+ -> followed by 0 or more occurence of digits in between 0-9
Havoc answered 20/12, 2020 at 13:2 Comment(0)
U
-4
str(strval).isdigit()

seems to be simple.

Handles values stored in as a string or int or float

Urquhart answered 24/9, 2016 at 2:20 Comment(4)
In [2]: '123,123'.isdigit() Out[2]: FalseTrimester
It does not work for negative numbers literal, please fix your answerEarthshine
'39.1'.isdigit()Solferino
all([x.isdigit() for x in str(VAR).strip('-').replace(',','.').split('.')]) If you are looking for a more complete implementation.Acuminate

© 2022 - 2024 — McMap. All rights reserved.