views:

129

answers:

5

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))
+3  A: 

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
Rafe Kettler
That's very nice, thanks. (Will leave this question open for a day just in case.)
Neil G
@NeilG: okay, let's see if a guru can descend on this question and find a more elegant solution :D
Rafe Kettler
no reason to use a lambda here, points are not awarded for shorter code, and it is otherwise exactly the same code. you even gave it a name...
TokenMacGuy
@TokenMacGuy : I just love lambdas.
Rafe Kettler
@Rafe Kettler - yes, but think of it like a narcotic. Sure, you love them. But one day you'll wake up on a concrete floor in a pool of your own sweat and think "why does this code only make sense when I read it backwards?"
detly
@detly that's one of the funniest things I've ever read
Rafe Kettler
+2  A: 

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.

S.Lott
Since he has to implement it himself anyway, can't he just implement it appropriately for his application? You're basically answering his question with a question.
Rafe Kettler
"Is there a function to test floating point approximate equality in python?" That question -- as asked -- is a bad question. The answer is a useless "No". Since the question is bad, we have to move on to a related question like "why not?" The answer to the more useful related question is "There can't be a function". As in singular. This isn't built-in because there's no point in building it in. You **always** have to implement it yourself. The "answer" is a useless "no, there is no function" which the questioner already knew.
S.Lott
+4  A: 

Another approach is to compute the relative difference of the two numbers, which is the "ratio of a difference to a baseline value". The two ways mentioned is the Wikipedia article can each be expressed like this in Python:

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

and

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 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).

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

martineau
+1  A: 

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.

TokenMacGuy
what about in tests where you calculate a value in two different ways?
Neil G
Can you elaborate on that?
TokenMacGuy
I will do that in the question.
Neil G
In that specific case, interval math is the way to go. each operation tracks the amount of round-off error it will cause.
TokenMacGuy