[Python-Dev] Floor division (original) (raw)

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 isminus0(x): ... import math ... return x == 0.0 and math.atan2(x, x) != 0 isminus0(+0.0) # just showing that "it works" False isminus0(-0.0) # ditto True isminus0(complex(1, 0.0).imag) False isminus0(complex(1, -0.0).imag) # should be True False

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

isminus0(complex(0.0, 0.0).real) False isminus0(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:

isminus0(complex(0, 0).conjugate().imag) True

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

isminus0(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