pythonhashfloating-pointequalityfloating-accuracy

Override python's float for inexact comparison - hash implementation


I run into an interesting problem - here is an override of float that compares inexactly:

class Rounder(float):
    """Float wrapper used for inexact comparison."""
    __slots__ = ()

    def __hash__(self):
        raise NotImplementedError
    def __eq__(self, b, rel_tol=1e-06, abs_tol=1e-12):
        """Check if the two floats are equal to the sixth place (relatively)
        or to the twelfth place (absolutely)."""
        try:
            return abs(self - b) <= max(rel_tol * max(abs(self), abs(b)),
                                        abs_tol) # could use math.isclose
        except TypeError:
            return NotImplemented

        ...

The requirement is that equal objects have equal hashes - but can't seem to to come up with a formula that represents all Rounder(float) instances that would compare the same (so map them all to the same hash value). Most of the advice in the web is on how to define hash/equals for classes that compare based on some (immutable) attributes - does not apply to this case.


Solution

  • There is no valid way to hash these objects. Hashing requires a transitive definition of ==, which your objects do not have. Even if you did something like def __hash__(self): return 0, intransitivity of == would still make your objects unsafe to use as dict keys.

    Non-transitivity is one of the big reasons not to define == this way. If you want to do a closeness check, do that explicitly with math.isclose. Don't make that operation ==.