Comparing floats

K

kj

I'm defining a class (Spam) of objects that are characterized by
three parameters, A, B, C, where A and C are n-tuples of floats
and B is an n*n tuple-of-tuples of floats. I'm defining a limited
multiplication for these objects, like this:

Spam(A, B, C) * Spam(D, E, F) = Spam(A, dot(B, E), F)
if and only if C == D.

(Here dot(B, E) represents the matrix multiplication of B and E).

In other words, this multiplication is defined only for the case
where the last parameter of the first object is equal to first
parameter of the second object. An exception should be thrown if
one attempts to multiply two objects that fail to meet this
requirement.

Therefore, to implement this multiplication operation I need to
have a way to verify that the float tuples C and D are "equal".
Certainly, I want to do this in in a way that takes into account
the fact machine computations with floats can produce small
differences between numbers that are notionally the same. E.g.
(in Python 2.6.1 at least):
False

The only approach I know of is to pick some arbitrary tolerance
epsilon (e.g. 1e-6) and declare floats x and y equal iff the absolute
value of x - y is less than epsilon.

Does the Python standard library provide any better methods for
performing such comparisons?

I understand that, in Python 2.7 and 3.x >= 3.1, when the interactive
shell displays a float it shows "the shortest decimal fraction that
rounds correctly back to the true binary value". Is there a way
to access this rounding functionality from code that must be able
to run under version 2.6? (The idea would be to implement float
comparison as a comparison of the rounded versions of floats.)

Absent these possibilities, does Python provide any standard value
of epsilon for this purpose?

TIA!

~kj
 
T

Terry Reedy

Therefore, to implement this multiplication operation I need to
have a way to verify that the float tuples C and D are "equal".

I might try the average relative difference:
sum(abs((i-j)/(i+j)) for i,j in zip(C,D))/n # assuming lengths constant
Certainly, I want to do this in in a way that takes into account
the fact machine computations with floats can produce small
differences between numbers that are notionally the same.

The problem is that the appropriate value may depend on the application
and the source of the floats. If they are measured to 3 decimal places,
you need a large value. If they are calculated, you need much smaller.
 
A

Arnaud Delobelle

Terry Reedy said:
I might try the average relative difference:
sum(abs((i-j)/(i+j)) for i,j in zip(C,D))/n # assuming lengths constant

That'll throw an exception if i == -j. You could replace (i+j) with
math.hypot(i, j) or abs(i)+abs(j) but it will still fail when i == j ==
0.
 
S

Steven D'Aprano

On Sat, 27 Nov 2010 22:55:10 +0000, kj wrote:

[...]
Therefore, to implement this multiplication operation I need to have a
way to verify that the float tuples C and D are "equal".

That C and D are tuples of floats is irrelevant. The problem boils down
to testing floats for equality.

It's easy to test two floats for equality, that's exactly what == does,
but two floats which should be equal might not be due to calculation
errors. To work around this, we loosen the definition of "equal" to give
some allowance for rounding errors. Unfortunately, you need to decide
what you mean by "two floats are equal", since that will depend on the
semantics of your problem and data. There is no standard answer that
applies everywhere.

I suggest you leave it up to the user to decide what tolerance their data
can support, and offer a sensible default for cases that they don't know
or don't care.

This might be useful for you, or at least give you some ideas:

http://code.activestate.com/recipes/577124-approximately-equal/



[...]
The only approach I know of is to pick some arbitrary tolerance epsilon
(e.g. 1e-6) and declare floats x and y equal iff the absolute value of x
- y is less than epsilon.

The four basic approaches are:

(1) Is the absolute difference between the values <= some tolerance?
(2) Is the relative difference between the values <= some tolerance?
(3) Round the two values to a fixed number of decimal places, then
compare for equality. This is a variation on (1) above.
(4) How many differences in the least significant bits of the two values
do we accept?


I understand that, in Python 2.7 and 3.x >= 3.1, when the interactive
shell displays a float it shows "the shortest decimal fraction that
rounds correctly back to the true binary value". Is there a way to
access this rounding functionality from code that must be able to run
under version 2.6? (The idea would be to implement float comparison as a
comparison of the rounded versions of floats.)

How do you expect to access code in the Python 2.7 interpreter from
Python 2.6? If you have 2.7 available, just use 2.7 :)

It is a standard, royalty-free algorithm that you can find on the
Internet somewhere. Worst case, you could copy it from the Python 2.7
source code, re-write it in Python if need be, and distribute it in your
own application. But I don't think it will help you, since it isn't
dealing with the fundamental problem that:

* equality between two floats is well-defined, but not useful

* equality given some tolerance is useful, but not well-defined (there is
no tolerance that is always best, and there is no general way to decide
whether absolute or relative error is more important)
 
P

Peter Otten

kj said:
I understand that, in Python 2.7 and 3.x >= 3.1, when the interactive
shell displays a float it shows "the shortest decimal fraction that
rounds correctly back to the true binary value". Is there a way
to access this rounding functionality from code that must be able
to run under version 2.6? (The idea would be to implement float
comparison as a comparison of the rounded versions of floats.)

Doesn't float(str(x)) == x for all x imply that str(x) == str(y) if and only
if x == y? If so, what would be the benefit of converting to string at all?
 
N

Nobody

I might try the average relative difference:
sum(abs((i-j)/(i+j)) for i,j in zip(C,D))/n # assuming lengths constant

The division is unstable if i and j are close to zero.

For scalars, I'd use:

abs(i-j) <= epsilon * (1 + abs(i+j))

This amounts to a relative error check for large values and an absolute
error check for values close to zero.

For a vector, I'd check that the above holds for all pairs.
 
M

Marco Nawijn

The division is unstable if i and j are close to zero.

For scalars, I'd use:

        abs(i-j) <= epsilon * (1 + abs(i+j))

This amounts to a relative error check for large values and an absolute
error check for values close to zero.

For a vector, I'd check that the above holds for all pairs.

Hi All,

Why don't you treat the C and D tuples as vectors? You can than check
dot product
and norm (length) for equality. Using scipy (www.scipy.org), you even
get very nice
performance.

Marco
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top