[Python-Dev] Comparing heterogeneous types

Andrew Koenig ark-mlist at att.net
Thu Jun 3 11:16:42 EDT 2004

> Do you think you can come up with a patch, or at least a description
> of an algorithm that someone without a wizard level understanding of
> the issues could implement?

The following code is lightly but not exhaustively tested, but I do think it
gives a good idea of the algorithm.  In particular, note that
compare(fmagic, lmagic+1) is -1 even though (fmagic==lmagic+1) yields True.

lmagic = 1
fmagic = 1.0
while fmagic + 1.0 != fmagic:
    fmagic += fmagic
    lmagic += lmagic
assert fmagic + 1.0 == fmagic
assert fmagic - 1.0 != fmagic

# fmagic and lmagic are the float and long representations of the
# smallest power of 2 for which the floating-point LSB is strictly
# greater than 1.  Accordingly, adding 1 to this magic value doesn't
# change it, but subtracting 1 does (because it is a power of 2, so
# the subtraction makes the representation one bit shorter).

# Because of the properties of fmagic and lmagic, converting an
# integer that is < lmagic to floating-point will lose no information,
# and converting a floating-point number that is >= fmagic to long
# will also lose no information.

def sign(x):
    if x > 0:
        return 1
    if x < 0:
        return -1
    return 0

def simple_compare(x, y):               # arguments both float or neither
    if x < y:
        return -1
    if x > y:
        return 1
    return 0

def compare(x, y):
    # We handle only integers and floats
    if type(x) not in [int, long, float]:
        raise TypeError, "left argument to compare not integer or float"
    if type(y) not in [int, long, float]:
        raise TypeError, "right argument to compare not integer or float"

    # If the arguments are the same type (counting int and long as
    # the same), there's nothing special to do
    if (type(x) == float) == (type(y) == float):
        return simple_compare(x, y)

    # Perhaps the signs will make the comparison unnecessary
    signx = sign(x)
    signy = sign(y)
    if signx != signy or signx == 0:
        return simple_compare(signx, signy)

    # Now we know that both arguments are nonzero and have the same
    # sign.  We'll use signx to remember the original sign and make
    # the arguments both positive for convenience.
    if signx < 0:
        x = -x
        y = -y

    # We have reduced the problem to one where the arguments are both
    # positive and exactly one of them is float--but we don't know
    # which one.  For convenience, we'll make x float by swapping x
    # and y if necessary, adjusting signx as appropriate.
    if type(y) == float:
        x, y = y, x
        signx = -signx

    assert type(x) == float
    assert type(y) in [int, long]
    assert x > 0 and y > 0

    # If x and y are on different sides of the magic barrier, we're
    # done.
    if x >= fmagic and y < lmagic:
        return signx
    if x < fmagic and y >= lmagic:
        return -signx

    # Now we know that x and y are on the same side of the barrier.
    # Depending on which side, we will convert x to long or y to
    # float, then compare.
    if x >= fmagic:
        x = long(x)
        y = float(y)
    return signx * simple_compare(x, y)

More information about the Python-Dev mailing list