Q: Feature Wish: "%" Extension

Tim Peters tim.one at home.com
Sun Nov 4 18:57:20 EST 2001


[Marcin 'Qrczak' Kowalczyk]
> Among these types in OCaml:
>   1. int: builtin, machine word size,
>   2. big_int: unbounded,
>   3. num: union of int, big_int and ratio,
> all have different meanings of division and modulus!

Yet another reason to avoid OCaml <wink?>.

> I wasn't aware that there are more than two variants in use.
>
> In each case separately division is consistent with modulus.
> The following conditions hold in the respective cases:
>   1. (-x) div y = -(x div y)
>   2. 0 <= x mod y < |y|
>   3. x div y = floor (x/y)
>
> Intel processors and C99 do the first variant. Python does the third
> variant. Knuth says the third variant is the true one. In SML and
> Haskell the first variant is called quot/rem and the third is called
> div/mod.
>
> I recently needed to express quot/rem and div/mod in terms of the
> first and the second variant. I think I haven't seen the second
> variant before.

Me neither.  There's a fourth variant, most useful with floating types,
where x%y is defined to be:

    x - round_to_nearest_or_even_integer(x/y) * y

where that expression is mathematical (exact; not using machine fp or
integer ops; the infinite-precision *result* is exactly representable as a
machine float).  The useful property that follows is:

    |x%y| <= |y|/2

and that's "useful" because floating mod is most often used for some flavor
of argument reduction, and you usually want the absolute value of "the
leftover part" to be as small as possible (e.g., to speed convergence of a
series expansion).

Python's choice sucks for floating mod, because x%y in Python cannot always
be exact.  For example, x = -1e-300, y = 1e300.  The exact "floor mod" of
x%y is then 1e300-1e-300, and floats are a few thousand bits shy of being
able to represent that <wink>.  So we get this absurdity instead:

>>> divmod(-1e-300, 1e300)
(-1.0, 1.0000000000000001e+300)
>>>

Curiously, the C99 "trunc mod" doesn't suffer this problem, basically
because the sign of its result is taken from x instead from y (so whenever
|x| < |y|, C99 x%y can return x without further ado; else |x| >= |y|, and
then exactness is easy (albeit tedious) to achieve).

So I'm not at all averse to different mods for different types, and actively
wish Python could change its story for float mod.  But having different
flavors of % for different integer types seems at best mildly nuts.

consistency-is-often-a-dumb-idea-ly y'rs  - tim





More information about the Python-list mailing list