542

It's well known that comparing floats for equality is a little fiddly due to rounding and precision issues. For examples on this, see the blog post Comparing Floating Point Numbers, 2012 Edition by Bruce Dawson.

How do I deal with this in Python?

Is a standard library function for this available somewhere?

10
  • @tolomea: Since it depends on your application and your data and your problem domain -- and it's only one line of code -- why would there be a "standard library function"?
    – S.Lott
    Commented Apr 8, 2011 at 13:23
  • 15
    @S.Lott: all, any, max, min are each basically one-liners, and they aren't just provided in a library, they're builtin functions. So the BDFL's reasons aren't that. The one line of code that most people write is pretty unsophisticated and often doesn't work, which is a strong reason to provide something better. Of course any module providing other strategies would have to also provide caveats describing when they're appropriate, and more importantly when they aren't. Numeric analysis is hard, it's no great disgrace that language designers usually don't attempt tools to help with it. Commented Apr 8, 2011 at 17:49
  • 7
    @S.Lott: I'd probably agree if the standard Python distribution didn't come with multiple modules for XML interfaces. Clearly the fact that different applications need to do something differently is no bar at all to putting modules in the base set to do it one way or another. Certainly there are tricks for comparing floats that get re-used a lot, the most basic being a specified number of ulps. So I only partially agree - the problem is that numeric analysis is hard. Python could in principle provide tools to make it somewhat easier, some of the time. I guess nobody has volunteered. Commented Apr 8, 2011 at 18:01
  • 4
    Also, "it boils down to one hard-to-design line of code" - if it's still a one-liner once you're doing it properly, I think your monitor is wider than mine ;-). Anyway, I think the whole area is quite specialized, in the sense that most programmers (including me) very rarely use it. Combined with being hard, it's not going to hit the top of the "most wanted" list for core libraries in most languages. Commented Apr 8, 2011 at 18:31
  • 1
    @S.Lott So far we really only seem to have two workable solutions, one of which is only valid if you know the magnitude of the inputs. Sure the epsilon value(s) need to be adjusted based on application, but they are arguments. Commented Apr 9, 2011 at 23:05

18 Answers 18

529

Python 3.5 adds the math.isclose and cmath.isclose functions as described in PEP 485.

If you're using an earlier version of Python, the equivalent function is given in the documentation.

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)

rel_tol is a relative tolerance, it is multiplied by the greater of the magnitudes of the two arguments; as the values get larger, so does the allowed difference between them while still considering them equal.

abs_tol is an absolute tolerance that is applied as-is in all cases. If the difference is less than either of those tolerances, the values are considered equal.

17
  • 52
    note when a or b is a numpy array, numpy.isclose works.
    – abcd
    Commented Oct 29, 2015 at 20:52
  • 6
    @marsh rel_tol is a relative tolerance, it is multiplied by the greater of the magnitudes of the two arguments; as the values get larger, so does the allowed difference between them while still considering them equal. abs_tol is an absolute tolerance that is applied as-is in all cases. If the difference is less than either of those tolerances, the values are considered equal. Commented Nov 11, 2015 at 23:16
  • 5
    Not to diminish the value of this answer (I think it's a good one), it's worth noting that the documentation also says: "Modulo error checking, etc, the function will return the result of..." In other words, the isclose function (above) is not a complete implementation.
    – rkersh
    Commented Jul 14, 2016 at 19:50
  • 5
    Apologies for reviving an old thread, but it seemed worth pointing out that isclose always adheres to the less conservative criterion. I only mention it because that behavior is counterintuitive to me. Were I to specify two criteria, I would always expect the smaller tolerance to supercede the greater. Commented Mar 21, 2017 at 13:57
  • 8
    @MackieMesser you're entitled to your opinion of course, but this behavior made perfect sense to me. By your definition nothing could ever be "close to" zero, because a relative tolerance multiplied by zero is always zero. Commented Mar 21, 2017 at 15:32
112

Something as simple as the following may be good enough:

return abs(f1 - f2) <= allowed_error
7
  • 13
    As the link I provided points out, subtracting only works if you know the approximate magnitude of the numbers in advance. Commented Apr 8, 2011 at 13:14
  • 16
    In my experience, the best method for comparing floats is: abs(f1-f2) < tol*max(abs(f1),abs(f2)). This sort of relative tolerance is the only meaningful way to compare floats in general, as they are usually affected by roundoff error in the small decimal places. Commented Feb 4, 2015 at 1:52
  • 7
    Just adding a simple example why it may not work: >>> abs(0.04 - 0.03) <= 0.01, it yields False. I use Python 2.7.10 [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
    – schatten
    Commented Sep 21, 2015 at 0:28
  • 3
    @schatten to be fair, that example has more to do with machine binary precision/formats than the particular comparison algo. When you entered 0.03 into the system, that's not really the number that made it to the CPU. Commented Sep 21, 2015 at 1:31
  • 7
    @AndrewWhite that example shows that abs(f1 - f2) <= allowed_error does not work as expected.
    – schatten
    Commented Sep 21, 2015 at 1:35
77

I would agree that Gareth's answer is probably most appropriate as a lightweight function/solution.

But I thought it would be helpful to note that if you are using NumPy or are considering it, there is a packaged function for this.

numpy.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)

A little disclaimer though: installing NumPy can be a non-trivial experience depending on your platform.

4
19

Use Python's decimal module, which provides the Decimal class.

From the comments:

It is worth noting that if you're doing math-heavy work and you don't absolutely need the precision from decimal, this can really bog things down. Floats are way, way faster to deal with, but imprecise. Decimals are extremely precise but slow.

0
17

math.isclose() has been added to Python 3.5 for that (source code). Here is a port of it to Python 2. It's difference from one-liner of Mark Ransom is that it can handle "inf" and "-inf" properly.

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    '''
    Python 2 implementation of Python 3.5 math.isclose()
    https://github.com/python/cpython/blob/v3.5.10/Modules/mathmodule.c#L1993
    '''
    # sanity check on the inputs
    if rel_tol < 0 or abs_tol < 0:
        raise ValueError("tolerances must be non-negative")

    # short circuit exact equality -- needed to catch two infinities of
    # the same sign. And perhaps speeds things up a bit sometimes.
    if a == b:
        return True

    # This catches the case of two infinities of opposite sign, or
    # one infinity and one finite number. Two infinities of opposite
    # sign would otherwise have an infinite relative tolerance.
    # Two infinities of the same sign are caught by the equality check
    # above.
    if math.isinf(a) or math.isinf(b):
        return False

    # now do the regular computation
    # this is essentially the "weak" test from the Boost library
    diff = math.fabs(b - a)
    result = (((diff <= math.fabs(rel_tol * b)) or
               (diff <= math.fabs(rel_tol * a))) or
              (diff <= abs_tol))
    return result
16

The common wisdom that floating-point numbers cannot be compared for equality is inaccurate. Floating-point numbers are no different from integers: If you evaluate "a == b", you will get true if they are identical numbers and false otherwise (with the understanding that two NaNs are of course not identical numbers).

The actual problem is this: If I have done some calculations and am not sure the two numbers I have to compare are exactly correct, then what? This problem is the same for floating-point as it is for integers. If you evaluate the integer expression "7/3*3", it will not compare equal to "7*3/3".

So suppose we asked "How do I compare integers for equality?" in such a situation. There is no single answer; what you should do depends on the specific situation, notably what sort of errors you have and what you want to achieve.

Here are some possible choices.

If you want to get a "true" result if the mathematically exact numbers would be equal, then you might try to use the properties of the calculations you perform to prove that you get the same errors in the two numbers. If that is feasible, and you compare two numbers that result from expressions that would give equal numbers if computed exactly, then you will get "true" from the comparison. Another approach is that you might analyze the properties of the calculations and prove that the error never exceeds a certain amount, perhaps an absolute amount or an amount relative to one of the inputs or one of the outputs. In that case, you can ask whether the two calculated numbers differ by at most that amount, and return "true" if they are within the interval. If you cannot prove an error bound, you might guess and hope for the best. One way of guessing is to evaluate many random samples and see what sort of distribution you get in the results.

Of course, since we only set the requirement that you get "true" if the mathematically exact results are equal, we left open the possibility that you get "true" even if they are unequal. (In fact, we can satisfy the requirement by always returning "true". This makes the calculation simple but is generally undesirable, so I will discuss improving the situation below.)

If you want to get a "false" result if the mathematically exact numbers would be unequal, you need to prove that your evaluation of the numbers yields different numbers if the mathematically exact numbers would be unequal. This may be impossible for practical purposes in many common situations. So let us consider an alternative.

A useful requirement might be that we get a "false" result if the mathematically exact numbers differ by more than a certain amount. For example, perhaps we are going to calculate where a ball thrown in a computer game traveled, and we want to know whether it struck a bat. In this case, we certainly want to get "true" if the ball strikes the bat, and we want to get "false" if the ball is far from the bat, and we can accept an incorrect "true" answer if the ball in a mathematically exact simulation missed the bat but is within a millimeter of hitting the bat. In that case, we need to prove (or guess/estimate) that our calculation of the ball's position and the bat's position have a combined error of at most one millimeter (for all positions of interest). This would allow us to always return "false" if the ball and bat are more than a millimeter apart, to return "true" if they touch, and to return "true" if they are close enough to be acceptable.

So, how you decide what to return when comparing floating-point numbers depends very much on your specific situation.

As to how you go about proving error bounds for calculations, that can be a complicated subject. Any floating-point implementation using the IEEE 754 standard in round-to-nearest mode returns the floating-point number nearest to the exact result for any basic operation (notably multiplication, division, addition, subtraction, square root). (In case of tie, round so the low bit is even.) (Be particularly careful about square root and division; your language implementation might use methods that do not conform to IEEE 754 for those.) Because of this requirement, we know the error in a single result is at most 1/2 of the value of the least significant bit. (If it were more, the rounding would have gone to a different number that is within 1/2 the value.)

Going on from there gets substantially more complicated; the next step is performing an operation where one of the inputs already has some error. For simple expressions, these errors can be followed through the calculations to reach a bound on the final error. In practice, this is only done in a few situations, such as working on a high-quality mathematics library. And, of course, you need precise control over exactly which operations are performed. High-level languages often give the compiler a lot of slack, so you might not know in which order operations are performed.

There is much more that could be (and is) written about this topic, but I have to stop there. In summary, the answer is: There is no library routine for this comparison because there is no single solution that fits most needs that is worth putting into a library routine. (If comparing with a relative or absolute error interval suffices for you, you can do it simply without a library routine.)

1
14

I'm not aware of anything in the Python standard library (or elsewhere) that implements Dawson's AlmostEqual2sComplement function. If that's the sort of behaviour you want, you'll have to implement it yourself. (In which case, rather than using Dawson's clever bitwise hacks you'd probably do better to use more conventional tests of the form if abs(a-b) <= eps1*(abs(a)+abs(b)) + eps2 or similar. To get Dawson-like behaviour you might say something like if abs(a-b) <= eps*max(EPS,abs(a),abs(b)) for some small fixed EPS; this isn't exactly the same as Dawson, but it's similar in spirit.

9
  • I don't quite follow what you are doing here, but it is interesting. What is the difference between eps, eps1, eps2 and EPS? Commented Apr 8, 2011 at 13:19
  • eps1 and eps2 define a relative and an absolute tolerance: you're prepared to allow a and b to differ by about eps1 times how big they are plus eps2. eps is a single tolerance; you're prepared to allow a and b to differ by about eps times how big they are, with the proviso that anything of size EPS or smaller is assumed to be of size EPS. If you take EPS to be the smallest non-denormal value of your floating-point type, this is very similar to Dawson's comparator (except for a factor of 2^#bits because Dawson measures tolerance in ulps). Commented Apr 8, 2011 at 14:22
  • 2
    Incidentally, I agree with S. Lott that the Right Thing is always going to depend on your actual application, which is why there isn't a single standard library function for all your floating-point comparison needs. Commented Apr 8, 2011 at 14:23
  • @gareth-mccaughan How does one determine the "smallest non-denormal value of your floating-point type" for python? Commented Apr 9, 2011 at 23:09
  • This page docs.python.org/tutorial/floatingpoint.html says almost all python implementations use IEEE-754 double precision floats and this page en.wikipedia.org/wiki/IEEE_754-1985 says the normalized numbers closest to zero are ±2**−1022. Commented Apr 9, 2011 at 23:34
11

If you want to use it in testing/TDD context, I'd say this is a standard way:

from nose.tools import assert_almost_equals

assert_almost_equals(x, y, places=7) # The default is 7
8

In terms of absolute error, you can just check

if abs(a - b) <= error:
    print("Almost equal")

Some information of why float act weird in Python: Python 3 Tutorial 03 - if-else, logical operators and top beginner mistakes

You can also use math.isclose for relative errors.

5

This is useful for the case where you want to make sure two numbers are the same 'up to precision', and there isn't any need to specify the tolerance:

  • Find minimum precision of the two numbers

  • Round both of them to minimum precision and compare

def isclose(a, b):
    astr = str(a)
    aprec = len(astr.split('.')[1]) if '.' in astr else 0
    bstr = str(b)
    bprec = len(bstr.split('.')[1]) if '.' in bstr else 0
    prec = min(aprec, bprec)
    return round(a, prec) == round(b, prec)

As written, it only works for numbers without the 'e' in their string representation (meaning 0.9999999999995e-4 < number <= 0.9999999999995e11)

Example:

>>> isclose(10.0, 10.049)
True
>>> isclose(10.0, 10.05)
False
3
  • 2
    The unbounded concept of close will not serve you well. isclose(1.0, 1.1) produces False, and isclose(0.1, 0.000000000001) returns True.
    – kfsone
    Commented Oct 14, 2019 at 21:17
  • I think there is a bit of a misconception in this answer. When a physicist chooses to write 10.049 instead of 10.05, yes, it means they have more precision, so it makes sense to compare "up to precision". But when you do astr = str(a), the number of digits in astr don't match any concept of "precision". For instance, str(1/3) is '0.3333333333333333' and has lots of digits but str(1/4) is '0.25' and has only two digits. In particular, a physicist can choose whether to write 10.050 or 10.05, but str(a) will never return '10.050' no matter the value or "precision" of a.
    – Stef
    Commented Feb 2 at 12:27
  • If you want to account for this "precision" concept in python you need to do it explicitly, for instance by defining your own class numberWithPrecision that maintains two fields val and prec, and defining a == b as True iff the two intervals [a.val - a.prec, a.val + a.prec] and [b.val - b.prec, b.val + b.prec] have nonempty intersection. Then you can define a + b as return numberWithPrecision(a.val + b.val, a.prec + b.prec) and a * b as return numberWithPrecision(a.val * b.val, a.prec * b.val + a.val * b.prec + a.prec * b.prec)
    – Stef
    Commented Feb 2 at 12:31
2

For some of the cases where you can affect the source number representation, you can represent them as fractions instead of floats, using integer numerator and denominator. That way you can have exact comparisons.

See Fraction from fractions module for details.

2

I found the following comparison helpful:

str(f1) == str(f2)
6
  • it's interesting, but not very practical due to str(.1 + .2) == .3 Commented May 26, 2015 at 21:06
  • str(.1 + .2) == str(.3) returns True
    – kantuni
    Commented Mar 12, 2016 at 22:38
  • 1
    How is this any different from f1 == f2 -- if they're both close but still different due to precision, the string representations will also be unequal.
    – MrMas
    Commented Jan 11, 2017 at 16:43
  • 2
    .1 + .2 == .3 returns False while str(.1 + .2) == str(.3) returns True
    – Kresimir
    Commented Jan 12, 2017 at 16:35
  • 6
    In Python 3.7.2, str(.1 + .2) == str(.3) returns False. The method described above works only for Python 2.
    – Danibix
    Commented Mar 23, 2019 at 16:29
2

If you want to do it in a testing or TDD context using the pytest package, here's how:

import pytest


PRECISION = 1e-3

def assert_almost_equal():
    obtained_value = 99.99
    expected_value = 100.00
    assert obtained_value == pytest.approx(expected_value, PRECISION)
2

I liked Sesquipedal's suggestion, but with modification (a special use case when both values are 0 returns False). In my case, I was on Python 2.7 and just used a simple function:

if f1 ==0 and f2 == 0:
    return True
else:
    return abs(f1-f2) < tol*max(abs(f1),abs(f2))
0

To compare up to a given decimal without atol/rtol:

def almost_equal(a, b, decimal=6):
    return '{0:.{1}f}'.format(a, decimal) == '{0:.{1}f}'.format(b, decimal)

print(almost_equal(0.0, 0.0001, decimal=5)) # False
print(almost_equal(0.0, 0.0001, decimal=4)) # True 
0

This may be a bit ugly hack, but it works pretty well when you don't need more than the default float precision (about 11 decimals).

The round_to function uses the format method from the built-in str class to round up the float to a string that represents the float with the number of decimals needed, and then applies the eval built-in function to the rounded float string to get back to the float numeric type.

The is_close function just applies a simple conditional to the rounded up float.

def round_to(float_num, prec):
    return eval("'{:." + str(int(prec)) + "f}'.format(" + str(float_num) + ")")

def is_close(float_a, float_b, prec):
    if round_to(float_a, prec) == round_to(float_b, prec):
        return True
    return False

>>>a = 10.0
10.0
>>>b = 10.0001
10.0001
>>>print is_close(a, b, prec=3)
True
>>>print is_close(a, b, prec=4)
False

Update:

As suggested by @stepehjfox, a cleaner way to build a rount_to function avoiding "eval" is using nested formatting:

def round_to(float_num, prec):
    return '{:.{precision}f}'.format(float_num, precision=prec)

Following the same idea, the code can be even simpler using the great new f-strings (Python 3.6+):

def round_to(float_num, prec):
    return f'{float_num:.{prec}f}'

So, we could even wrap it up all in one simple and clean 'is_close' function:

def is_close(a, b, prec):
    return f'{a:.{prec}f}' == f'{b:.{prec}f}'
4
  • 1
    You don't have to use eval() to get parameterized formatting. Something like return '{:.{precision}f'.format(float_num, precision=decimal_precision) should do it Commented Jul 12, 2019 at 17:43
  • 1
    Source for my comment and more examples: pyformat.info/#param_align Commented Jul 12, 2019 at 17:46
  • 1
    Thanks @stephenjfox I didn't knew about nested formatting. Btw, your sample code lacks the ending curly braces: return '{:.{precision}}f'.format(float_num, precision=decimal_precision) Commented Dec 8, 2019 at 20:35
  • 1
    Good catch, and especially well done enhancement with the f-strings. With the death of Python 2 around the corner, maybe this will become the norm Commented Dec 9, 2019 at 17:00
-2

If you want to compare floats, the options above are great, but in my case, I ended up using Enum's, since I only had few valid floats my use case was accepting.

from enum import Enum
class HolidayMultipliers(Enum):
    EMPLOYED_LESS_THAN_YEAR = 2.0
    EMPLOYED_MORE_THAN_YEAR = 2.5

Then running:

testable_value = 2.0
HolidayMultipliers(testable_value)

If the float is valid, it's fine, but otherwise it will just throw an ValueError.

-3

Use == is a simple good way, if you don't care about tolerance precisely.

# Python 3.8.5
>>> 1.0000000000001 == 1
False
>>> 1.00000000000001 == 1
True

But watch out for 0:

>>> 0 == 0.00000000000000000000000000000000000000000001
False

The 0 is always the zero.


Use math.isclose if you want to control the tolerance.

The default a == b is equivalent to math.isclose(a, b, rel_tol=1e-16, abs_tol=0).


If you still want to use == with a self-defined tolerance:

>>> class MyFloat(float):
        def __eq__(self, another):
        return math.isclose(self, another, rel_tol=0, abs_tol=0.001)

>>> a == MyFloat(0)
>>> a
0.0
>>> a == 0.001
True

So far, I didn't find anywhere to config it globally for float. Besides, mock is also not working for float.__eq__.

2
  • 1
    You can't configure it globally cause it's not applying a tolerance it's comparing the actual bit values. While C Python uses C doubles this is not required in the spec, it may change in the future and other Python variants may do other things. So comparing floats with == may do different things depending on environment. Commented May 11, 2021 at 8:46
  • Yes, I was wrong. 1 + 1e-16 == 1 in Python, just because 1 + 1e-16 is 1.0 after precision lost.
    – Yan QiDong
    Commented May 11, 2021 at 9:21

Not the answer you're looking for? Browse other questions tagged or ask your own question.