BigInt divMod is wrong for negative first argument
Timon Gehr
timon.gehr at gmx.ch
Fri Aug 18 12:19:04 UTC 2023
On 8/18/23 13:44, NonNull wrote:
> divMod should be called divRem if the remainder part does what % does
> for negative integers, which is itself a mistake introducing futile
> complications, i.e. multiple cases in applications for no reason. All of
> this likely because of a silly definition of remainder that lingers in
> early education.
> ...
The actual source is that `/` rounds towards zero. `%` is then just
defined to be compatible with that.
This was copied from C. Its use in C firmly embedded it into the
hardware as a built-in instruction. I agree that this was a design
mistake, but it seems we are kind of stuck with this now.
> e.g. a%2==1 should test for odd numbers but stupidly fails for negative a.
> More generally a%m==b%m should test for a being congruent to b modulo m
> but fails if the signs of a,b are different.
>
> If % was actually mod these would work.
I think it would also be confusing if BigInt operations were
incompatible with operations on `int` when operating on values within
the range of `int`.
> For these to work a%m has to be periodic.
>
> e.g. a%2 should be ...,0,1,0,1,0,1,... as a runs through the integers,
> including in the negative region. Right now it is
> ...,-1,0,-1,0,-1,0,1,0,1,0,1,... for no good reason.
> More generally, a%m for positive m should be periodic, producing
> ...,0,1,...,m-1,0,1,...,m-1,... to ensure that a%m==b%m means that a is
> congruent to b modulo m.
>
> This is what mod means, the correct and periodic version of remainder
> for negative first arguments. To be useful, divMod should compute mod
> like this. And define div so that
>
> a = (a div m)*m + (a mod m)
> ...
Well, as I stated above, it is defined in this manner, but `/` in C (and
therefore D), rounds towards zero.
I do think it makes sense to define integer division such that it rounds
towards negative infinity. E.g., Python does this.
> i.e. a can be reassembled from a quotient and a sanely defined remainder.
>
> There is also good reason to take the value of (a mod m) to be
> independent of the sign of m so that it is the canonical representative
> of the congruence class of a modulo m, which doesn't change when the
> sign of m is changed because a is congruent to b modulo m iff a is
> congruent to b modulo (-m).
> ...
This I think is more debatable, as congruence classes of different
moduli need not be compatible. The corresponding integer division
operator would round towards negative or positive infinity based on the
sign of `m`. I guess a benefit of this is that you can move signs out of
and into fractions freely, but it seems it might be unexpected behavior.
In any case, I think it is much less natural to have a negative divisor
than to have a negative dividend when you are working with congruence
classes, so this point seems a bit less important.
> These choices could have informed the definition of divMod and made it
> immediately useful in applications. The existing divMod is misleading as
> it is divRem and not documented as such.
Probably.
> The Mod in divMod isn't mod!
>
>
Unfortunately, `%` is still often called a "modulo" operator.
Another issue is that `x % 0` crashes with a division by zero error,
though naturally the result would be just `x`.
More information about the Digitalmars-d
mailing list