What is the best way to compare floats for almost-equality in Python?

后端 未结 15 1452
旧时难觅i
旧时难觅i 2020-11-21 05:07

It\'s well known that comparing floats for equality is a little fiddly due to rounding and precision issues.

For example: https://randomascii.wordpress.com/2012/02/2

相关标签:
15条回答
  • 2020-11-21 05:43

    Is something as simple as the following not good enough?

    return abs(f1 - f2) <= allowed_error
    
    0 讨论(0)
  • 2020-11-21 05:47

    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://hg.python.org/cpython/file/tip/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
    
    0 讨论(0)
  • 2020-11-21 05:49

    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 讨论(0)
  • 2020-11-21 05:52

    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.

    0 讨论(0)
  • 2020-11-21 05:56

    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 讨论(0)
  • 2020-11-21 05:57

    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.

    0 讨论(0)
提交回复
热议问题