Testing floating point equality

Question:

Is there a function to test floating point approximate equality in python? Something like,

 def approx_equal(a, b, tol):
     return abs(a - b) < tol

My use case is similar to how Google’s C++ testing library, gtest.h, defines EXPECT_NEAR.

Here is an example:

def bernoulli_fraction_to_angle(fraction):
    return math.asin(sqrt(fraction))
def bernoulli_angle_to_fraction(angle):
    return math.sin(angle) ** 2
def test_bernoulli_conversions():
    assert(approx_equal(bernoulli_angle_to_fraction(pi / 4), 0.5, 1e-4))
    assert(approx_equal(
              bernoulli_fraction_to_angle(bernoulli_angle_to_fraction(0.1)),
                0.1, 1e-4))
Asked By: Neil G

||

Answers:

If I were you, I’d just use what you wrote, and either put it in a separate module (perhaps with other utilities you need that Python doesn’t have an implementation for) or at the top of whatever code requires it.

You can also use a lambda expression (one of my favorite language features, but probably less clear):

approx_equal = lambda a, b, t: abs(a - b) < t
Answered By: Rafe Kettler

Is there a function to test floating point approximate equality in python?

There can’t be a function, since the definition depends on context.

def eq( a, b, eps=0.0001 ):
    return abs(a - b) <= eps

Doesn’t always work. There are circumstances where

def eq( a, b, eps=0.0001 ):
     return abs( a - b ) / abs(a) <= eps

could be more appropriate.

Plus, there’s the always popular.

def eq( a, b, eps=0.0001 ):
    return abs(math.log( a ) - math.log(b)) <=  eps

Which might be more appropriate.

I can’t see how you can ask for a (single) function do combine all the mathematical alternatives. Since it depends on the application.

Answered By: S.Lott

Another approach is to compute the relative change (or relative difference) of the two numbers, which is “used to compare two quantities while taking into account the ‘sizes’ of the things being compared”. The two formulas mentioned in the Wikipedia article could be used in comparisons like the following in Python, which also handle cases where one or both of the values being compared are zero:

def approx_equal(a, b, tol):
    return abs(a-b) <= max(abs(a), abs(b)) * tol

def approx_equal(a, b, tol):
    return abs(a-b) <= (abs(a)+abs(b))/2 * tol

The calculated value in either case is a unitless fraction. In the first case the baseline value is the maximum absolute value of the two numbers and in the second it’s their mean absolute value. The article discusses each in more detail as well as their pros and cons. The latter can turned into a percentage difference if multiplied by 100 before the comparison (with tol becoming a percentage value). Note that the article suggests that if the changing value “is a percentage itself, it is better to talk about its change by using percentage points” — i.e. absolute change.

Both of these methods (obviously) require a little more computation than simply taking the absolute value of the difference of the two numbers, which might be a consideration.

Answered By: martineau

Comparing floats for equality is just usually a bad idea. Even with the tolerance feature you’re using, this isn’t really what you want to do.

If you want to use floats, a reasonable option is to refactor your algorithm to use inequalities, a < b because this is more likely to do what you expect, with far fewer false negatives or positives, and most importantly, it means you don’t have to guess how equal they must be for them to be equal.

If you can’t do that, another option is to use an exact representation. If your algorithm is composed only of arithmetic operations (+, -, * and /) then you can use a rational represntation, as provided by fractions.Fraction, or maybe decimal.Decimal is what you want (for instance, with financial calculations).

If your algorithm cannot be expressed easily with an arbitrary precision representation, another choice is to manage the roundoff error explicitly with interval arithmetic, for instance with this module.

  • For comparing numbers, there is math.isclose.
  • For comparing numbers or arrays, there is numpy.allclose.
  • For testing numbers or arrays, there is numpy.testing.assert_allclose
Answered By: Neil G

According to the tutorial:

… Though the numbers cannot be made closer to their intended exact values, the round() function can be useful for post-rounding so that results with inexact values become comparable to one another…

Therefore, this is the way that I define “isclose” functions in Python:

def isclose(a, b, ndigits):
   return round(a-b, ndigits) == 0

I usually use 5 as ndigits; However, it depends on the precision that you expect.

Answered By: Saeid BK