Comparing Floats - Same Number, But Does Not Equal

Comparing floats - same number, but does not equal?

floating point numbers have limited precision. view the warning about comparing them here:

http://php.net/manual/en/language.types.float.php

Is comparing two same literal float numbers for equality wrong?

There's a rule of thumb that you should apply to all programming rules of thumb (rule of thumbs?):

They are oversimplified, and will result in boneheaded decision making if pushed too far. IF you do not fully -grok- the intent behind the rule of thumb, you will mess up. Perhaps the rule of thumb remains a net positive (applying it without thought will improve things more than it will make them worse), but it will cause damage, and in any case it cannot be used as an argument in a debate.

So, with that in mind, clearly, there is no point in asking the question:

"Giving that the rule of thumb 'do not use == to compare floats' exists, is it ALWAYS bad?".

The answer is the extremely obvious: Duh, no. It's not ALWAYS bad, because rules of thumb pretty much by definition, if not by common sense, never ALWAYS apply.

So let's break it down then.

WHY is there a rule of thumb that you shouldn't == compare floats?

Your question suggests you already know this: It's because doing any math on floating points as represented by IEEE754 concepts such as java's double or float are inexact (vs. concepts like java's BigDecimal, which is exact *).

Do what you should always do when faced with a rule of thumb that, upon grokking why the rule of thumb exists and realizing it does not apply to your scenario: Completely ignore it.

Perhaps your question boils down to: I THINK I grok the rule of thumb, but perhaps I'm missing something; aside from the 'floating point math introduces small deviations which mess up == comparison', which does not apply to this case, are there any other reasons for this rule of thumb that I am not aware of?

In which case, my answer is: As far as I know, no.

*) But BigDecimal has its own equality problems, such as: Are two BigDecimal objects that represent the same mathematical number precisely, but which are configured to render at a different scale 'equal'? That depends on whether your viewpoint is that they are numbers or objects representing an exact decimal point number along with some meta properties including how to render it and how to round things if explicitly asked to do so. For what it is worth, the equals implementation of BD, which has to make a sophie's choice and choose between 2 equally valid interpretations of what equality means, chooses 'I represent a number', not 'I represent a number along with a bunch of metadata'. The same sophie's choice exists in all JPA/Hibernate stacks: Does a JPA object represent 'a row in the database' (thus equality being defined solely by the primary key value, and if not saved yet, two objects cannot be equal, not even to itself, unless the same reference identity), or does it represent the thing that the row represents, e.g. a student, and not 'a row in the DB that represents a student', in which case unid is the one field that does NOT matter for identity, and all the others (name, birthdate, social security number, etc) do. equality is hard.

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

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.

How should I do floating point comparison?

Comparing for greater/smaller is not really a problem unless you're working right at the edge of the float/double precision limit.

For a "fuzzy equals" comparison, this (Java code, should be easy to adapt) is what I came up with for The Floating-Point Guide after a lot of work and taking into account lots of criticism:

public static boolean nearlyEqual(float a, float b, float epsilon) {
final float absA = Math.abs(a);
final float absB = Math.abs(b);
final float diff = Math.abs(a - b);

if (a == b) { // shortcut, handles infinities
return true;
} else if (a == 0 || b == 0 || diff < Float.MIN_NORMAL) {
// a or b is zero or both are extremely close to it
// relative error is less meaningful here
return diff < (epsilon * Float.MIN_NORMAL);
} else { // use relative error
return diff / (absA + absB) < epsilon;
}
}

It comes with a test suite. You should immediately dismiss any solution that doesn't, because it is virtually guaranteed to fail in some edge cases like having one value 0, two very small values opposite of zero, or infinities.

An alternative (see link above for more details) is to convert the floats' bit patterns to integer and accept everything within a fixed integer distance.

In any case, there probably isn't any solution that is perfect for all applications. Ideally, you'd develop/adapt your own with a test suite covering your actual use cases.

comparing float/double values using == operator

IBM has a recommendation for comparing two floats, using division rather than subtraction - this makes it easier to select an epsilon that works for all ranges of input.

if (abs(a/b - 1) < epsilon)

As for the value of epsilon, I would use 5.96e-08 as given in this Wikipedia table, or perhaps 2x that value.

Example of seemingly equal float variables not equal

If you don't insist on float (Single) type, but actually want any floating point types (Single, Double) example you can try:

   if (Math.Sqrt(2.0) * Math.Sqrt(2.0) == 2.0) 
Console.Write("Equal, as expected");
else
Console.Write("Surprisingly not equal");


Related Topics



Leave a reply



Submit