[Python-ideas] Way to check for floating point "closeness"?

Mark Dickinson dickinsm at gmail.com
Tue Jan 13 10:28:39 CET 2015

On Tue, Jan 13, 2015 at 1:34 AM, Steven D'Aprano <steve at pearwood.info>

> Unfortunately a naive ULP comparison has trouble with NANs, INFs, and
> numbers close to zero, especially if they have opposite signs. The
> smallest representable denormalised floats larger, and smaller, than
> zero are:
> 5e-324
> -5e-324
> These are the smallest magnitude floats apart from zero, so we might
> hope that they are considered "close together", but they actually differ
> by 9223372036854775808 ULP. Ouch.

Only with a naive (i.e., wrong :-) implementation. Those two floats differ
by precisely 2 units in the last place, and any correct implementation
should report that.  It's not hard to write code that deals correctly with
opposite signs.  Here's a simple difference_in_ulps function that correctly
reports the number of ulps difference between any two finite floats.

>>> import struct

>>> def to_ulps(x):

...     n = struct.unpack('<q', struct.pack('<d', x))[0]

...     return -(n + 2**63) if n < 0 else n


>>> def difference_in_ulps(x, y):

...     return abs(to_ulps(x) - to_ulps(y))


>>> difference_in_ulps(-5e-324, 5e-324)


This is almost exactly what's in Lib/test/test_math.py already, except that
the function there is better documented and uses "~(n + 2**63)" instead of
"-(n + 2**63)" in the negative n correction branch, which has the effect of
regarding 0.0 and -0.0 as 1 ulp apart.

Comparing by ulps was what I needed for testing library-quality functions
for the math and cmath modules; I doubt that it's what's needed for most
comparison tasks.  I'd expect the suggested combination of relative error
and absolute error to be more appropriate most of the time.


> I have some ideas for dealing with that, and if anyone is interested I'm
> happy to talk about it, but they're not ready for production yet.
> I think that the Bruce Dawson is right. Floating point comparisons are
> hard, really hard. I know that I've still got a lot to learn about it. I
> can think of at least five different ways to compare floats for
> equality, and they all have their uses:
> - exact equality using ==
> - absolute error tolerances
> - relative error tolerances
> - ULP comparisons
> - the method unittest uses, using round()
> I'm explicitly including == because it is a floating point superstition
> that one should never under any circumstances compare floats for exact
> equality. As general advice, "don't use == unless you know what you are
> doing" is quite reasonable, but it's the "never use" that turns it into
> superstition. As Bruce Dawson says, "Floating-point numbers aren’t
> cursed", and throwing epsilons into a problem where no epsilon is needed
> is a bad idea.
> https://randomascii.wordpress.com/2012/06/26/doubles-are-not-floats-so-dont-compare-them/
> Aside: I'm reminded of APL, which mandates fuzzy equality (i.e. with a
> tolerance) of floating point numbers:
>     In an early talk Ken [Iverson] was explaining the advantages
>     of tolerant comparison. A member of the audience asked
>     incredulously, “Surely you don’t mean that when A=B and B=C,
>     A may not equal C?” Without skipping a beat, Ken replied,
>     “Any carpenter knows that!” and went on to the next question.
>     - Paul Berry
> --
> Steve
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20150113/d978b88a/attachment.html>

More information about the Python-ideas mailing list