[Python-Dev] Floor division

Tim Peters tim.peters at gmail.com
Tue Jan 23 08:01:35 CET 2007


[Guido]
> ...
> So you are proposing that Decimal also rip out the % and // operators
> and __divmod__? WFM, but I don't know what Decimal users say (I'm not
> one).

Yes:  it's just as much a floating type as HW binary floats, and all
the same issues come up.  For example, decimal floats are just as
prone to the floor division surprise Raymond started this thread with;
e.g.,

>>> a
Decimal("2.172839486617283948661728393E+29")
>>> b
Decimal("1234567890123456789012345678")
>>> a / b
Decimal("176.0000000000000000000000000")
>>> a/b == 176
True
>>> a // b
Decimal("175")

That is, floor division of `a` by `b` isn't necessarily the same as
the floor of `a` divided by `b` for decimal floats either, and for
exactly the same reason as when using binary floats:  a/b can suffer a
rounding error due to finite precision, but floor division computes
the floor of the quotient "as if" infinite precision were available.
At least using `decimal` it's easy to /explain/ just by boosting the
precision:

>>> decimal.getcontext().prec *= 2
>>> a / b
Decimal("175.99999999999999999999999997731999979587999814250798308")

This shows quite clearly why a/b rounded up to exactly 176 when
working with half this precision.

There's also that the decimal __mod__ implementation is like math.fmod
for binary floats, not like Python's int/long __mod__.  Having just
one builtin meaning for numeric `%` as an infix operator is a good
thing, and the int/long meaning is both by far the most common use but
only "works" for types with exactly representable results (ints and
longs built in; rationals if someone adds them; ditto constructive
reals; ... -- but not floats).

> ...
> For ints and floats, real could just return self, and imag could
> return a 0 of the same type as self.

Cool!  Works for me.

> I guess the conjugate() function could also just return self (although I see
> that conjugate() for a complex with a zero imaginary part returns
> something whose imaginary part is -0; is that intentional?

That's wrong, if true:  it should return something with the opposite
sign on the imaginary part, whether or not that equals 0 (+0. and -0.
both "equal 0").

This is harder to check than it should be because it appears there's a
bug in the complex constructor (at least in Python 2.5):  complex(1.,
0.0) and complex(1., -0.0) both appear to create a complex with a +0
imaginary part:

>>> def is_minus_0(x):
...     import math
...     return x == 0.0 and math.atan2(x, x) != 0
>>> is_minus_0(+0.0)  # just showing that "it works"
False
>>> is_minus_0(-0.0)   # ditto
True
>>> is_minus_0(complex(1, 0.0).imag)
False
>>> is_minus_0(complex(1, -0.0).imag)  # should be True
False

OTOH, the complex constructor does respect the sign of the real part:

>>> is_minus_0(complex(0.0, 0.0).real)
False
>>> is_minus_0(complex(-0.0, 0.0).real)
True

complex_new() ends with:

	cr.real -= ci.imag;
	cr.imag += ci.real;

and I have no idea what that thinks it's doing.  Surely this isn't intended?!:

>>> complex(complex(1.0, 2.0), complex(10.0, 20.0))
(-19+12j)

WTF?  In any case, that's also what's destroying the sign of the
imaginary part in complex(1.0, -0.0).

Knowing that a -0 imaginary part can't be constructed in the obvious way:

>>> is_minus_0(complex(0, 0).conjugate().imag)
True

So conjugate() does flip the sign on a +0 imaginary part, and:

>>> is_minus_0(complex(0, 0).conjugate().conjugate().imag)
False

so it also flips the sign on a -0 imaginary part.  That's all as it should be.

Hmm.  You meant to ask something different, but I actually answered that too ;-)

> I'd rather not have to do that when the input is an int or float, what do you
> think?)

Returning `self` is fine with me for those, although, e.g., it does mean that

    (3).conjugate().imag
and
    (complex(3)).conjugate().imag

are distinguishable with enough pain.  I don't care.  I think of
integers and floats as "not having" an imaginary part more than as
having a 0 imaginary part (but happy to invent a 0 imaginary part if
someone asks for one).


More information about the Python-Dev mailing list