Modulo of negative numbers (2011)
torstencurdt.com
torstencurdt.com
The clearest solution is to disambiguate the two as Zig does.
I solve the problem by never using mod or division for that matter with negative numbers.
There are only 2 cases either you truncate to zero or you floor to negative infinity.
The zero behavior breaks the symmetry to help remember.
n * truncate(a/n) + rem(a,n) = a
n * floor(a/n) + mod(a,n) = a
Now suppose that "a" is any real, a/n shared by both. however truncate and floor will do different things to the negatives. truncate "moves" numbers to zero
..............................................................
>>.>>.>>.>>.>>.>>.>>.>>.>>.>>0<<.<<.<<.<<.<<.<<.<<.<<.<<.<<.<<
while floor "moves" number to -inf
..............................................................
<<.<<.<<.<<.<<.<<.<<.<<.<<.<<0<<.<<.<<.<<.<<.<<.<<.<<.<<.<<.<<
when we then multiply by n its clear the positive "a" is the same however for negative a floor < truncate and so mod is positive while rem is negative for a < 0 : floor (a/n) < truncate(a/n) unless n divides a then they are =
The other case is for negative n, but that just flips the division and multiplication twice which has the effect of changing floor to ceiling and truncate to zero be truncate away from zero which flips the mod sign, but leaves rem alone, because its symmetric about zero.Mod is "fun". I agree, I simply avoid negative numbers entirely, and handle them more explicitly when needed. That way it's language independent.
- RoundNearest
- RoundNearestTiesAway
- RoundNearestTiesUp
- RoundToZero — rem, div
- RoundFromZero
- RoundUp
- RoundDown — mod, fld (i.e. floor(x/y) but without incorrect corner cases)
Most languages have no way of doing most of these, but then again, they're mostly pretty useless. They're really only useful when you're pairing division with a specific kind of rounding with a remainder that needs to match. Example of whacky remainder behavior in the "familiar" RoundNearest mode (default rounding mode for floating point):
julia> [k => rem(k, 4, RoundNearest) for k=-6:6]
13-element Vector{Pair{Int64, Int64}}:
-6 => 2
-5 => -1
-4 => 0
-3 => 1
-2 => -2
-1 => -1
0 => 0
1 => 1
2 => 2
3 => -1
4 => 0
5 => 1
6 => -2
Wild, huh? Output range for modulus 4 is -2:2 and whether you get -2 or 2 alternates with each cycle around the ring. (Of course, Julia has comprehensive support for all of these because we're a bunch of nerds for this kind of thing.)Dear God no why?! I thought my confusion around modulus could not get any worse :)
Full demo: https://play.rust-lang.org/?version=stable&mode=debug&editio...
Haskell has separate mod and rem functions too.
Above and below the heading "Even or Odd", the <pre> blocks suffer from "scrollbar blindness" on Windows. https://web.archive.org/web/20200827132812/https://svenkadak...
> In other words, the result is self / rhs rounded to the integer q such that self >= q * rhs. If self > 0, this is equal to round towards zero (the default in Rust); if self < 0, this is equal to round towards +/- infinity.
Python gets it right. Division is simply flooring division, and modulo is defined to satisfy n = remainder + quotient * divisor. That immediately gets you all the nice behavior such as (-1) % 10 = 9, and 1 % -10 = -9.
fn main() {
dbg!(1_i32.rem_euclid(-10));
dbg!(-1_i32.rem_euclid(10));
}
[src/main.rs:2] 1_i32.rem_euclid(-10) = 1
[src/main.rs:3] -1_i32.rem_euclid(10) = -1
I found only this issue https://github.com/rust-lang/rust/issues/87970 but has no detailsHere's the discussion that lead to the implementation of those functions, from more recent to least recent,
* the tracking issue https://github.com/rust-lang/rust/issues/49048
* the RFC https://github.com/rust-lang/rfcs/pull/2169
* the internals discussion https://internals.rust-lang.org/t/mathematical-modulo-operat...
It's baffling that Rust got this wrong..
The reason it's incredibly unnatural is not obvious when looking at the remainder in isolation. But the remainder is always one half of a pair: remainder and quotient. The quotient associated with rem_euclid is batshit insane.
The quotient associated with Python's modulo definition is simply floor(a/b).
Note that I'm wording it very strongly when I say it is wrong. It follows its spec faithfully and isn't 'bugged', its spec is just poorly and unnaturally chosen.
-13 \equiv 5 \pmod 3
it is the case that: > -13 % 3 === 5 % 3
false
I think it's better to use a convention where for all a, b, m, if a and b are congruent mod m, then a%m === b%mFrom a mathematical standpoint, I've never seen a use for returning -2 here. I want something unique for the group, so I can ask e.g. `x mod 3 == y mod 3` and have it work regardless of whether x or/and y are negative.
((a % b) + b) % b
It feels like there should be a nicer way (apart from the conditional one), but I can't think of it. Can you prove there isn't one?The only useful behavior is that crossing 0 does not change the pattern. Change my mind.
Or do you mean if based on integer division? I don't know what is fastest to implement in hardware for that one to be honest.
Do you have a source for this? Obviously, the big instruction sets natively implement round-to-zero division because that's what everyone already uses, but I've always attributed that to historical accident rather than first principles.
divMod is for when you want Z_n, you actually care about the equivalence classes and their "canonical" representative.
quotRem is for symmetry. The values of the remainders are the same, but you get some sign flip for free if you want it.
So for example. Suppose you had some number of hours and you wanted to "convert to days", if you want symmetric outputs then quotRem will do that and divMod won't.
36 hours divMod 24(hours/day) = 1d + 12h
-36 hours divMod 24(hours/day) = -2d + 12h
36 hours quotRem 24(hours/day) = 1d + 12h
-36 hours quotRem 24(hours/day) = -1d - 12h
obviously the information is the same. 12 = -12 (mod 24) but the form is different.If you divide -13 by 3, the "quotient" is -5 and the "remainder" is 2.
You have this exactly backwards. If 13 divided by 3 answers the question how often does 3 fit into 13 and the answer is 4, then -13 / -3 should obviously also be 4. And -13 / 3 and 13 / -3 should be -4 as it makes no sense to say that 3 fits into 13 4 times but into -13 -5 times. Changing the sign of the argument changes the sign of the result, not the magnitude, or equivalently integer division rounds towards zero. The remainders follow from this and are all +1 or -1.
Modular arithmetic on the other hand partitions the integers into equivalence classes and represents each equivalence class with a canonical member, usually the smallest non-negative one. So the integers modulo 3 - and of course also modulo -3 - form the following three equivalence.
[0] => ..., -18, -15, -12, -9, -6, -3, 0, 3, 6, 9, 12, 15, ...
[1] => ..., -17, -14, -11, -8, -5, -2, 1, 4, 7, 10, 13, 16, ...
[2] => ..., -16, -13, -10, -7, -4, -1, 2, 5, 8, 11, 14, 17, ...
Therefore 13 belongs to the equivalence class with canonical representation 1 modulo ±3 and -13 to the one represented by 2.You see how Division Algorithm is capitalized in my comment? I wasn't just having a seizure. The Division Algorithm is a famous theorem (the name is very old) that is generally taken to define the concepts of "quotient" and "remainder".
By the standard approach, there is no such thing as a negative remainder.
> Modular arithmetic on the other hand partitions the integers into equivalence classes and represents each equivalence class with a canonical member
This is just false. Modular arithmetic doesn't represent each equivalence class with a canonical member. You either work with raw numbers ("1"), or you represent the equivalence classes directly ("[-2]").
Missed that. But this just shows that Euclidean division is a bad choice if you are dealing with dividing signed quantities, that sign changes cause magnitude changes makes no sense in that case.
This is just false. Modular arithmetic doesn't represent each equivalence class with a canonical member. You either work with raw numbers ("1"), or you represent the equivalence classes directly ("[-2]").
I don't really understand what you mean. If you say [13] - [24] = [-11] mod 3 that is certainly true, but what's the point then? Wouldn't you at least want [13] - [24] = [1] mod 3 even if you skip reducing [13] - [24] to [1] - [0] explicitly?
4/6 = 2/3. I'm not sure what you mean by "whats the point"
https://hackage.haskell.org/package/base-4.17.0.0/docs/src/G...
With \ as integer division