3 ms·
It's easy to remember if you apply the operation to whole number line instead of trying to remember the cases. There are only 2 cases either you truncate to ze
by nh23423fefe 4y ago
It's easy to remember if you apply the operation to whole number line instead of trying to remember the cases.
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.
- Groxx 4y agoThere are only two cases... except when there are four, as the article covers. With wildly different conclusions by different languages, so there is no one interpretation / sane mental model that can be applied. Mod is "fun". I agree, I simply avoid negative numbers entirely, and handle them more explicitly when needed. That way it's language independent.
- StefanKarpinski 4y agoThere are actually as many cases as there are rounding modes, which is seven, and for most of them unlike `mod` and `rem` there's no common name for these remainder operations: - 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.)
- rightbyte 4y ago> rem (3, 4) => -1 Dear God no why?! I thought my confusion around modulus could not get any worse :)
- FabHK 4y agoJulia has functions divrem and fldmod that each return the quotient and remainder, with one or the other convention.
- StefanKarpinski 4y agoThe `rem`, `div` and `divrem` functions also all optionally take a rounding mode argument that lets you have the behavior that matches any rounding mode, where `RoundToZero` matches `div` and `RoundDown` matches `mod`, but there are actually a total of seven rounding modes. Most of them are pretty useless, but if you need some style of division and the remainder to match, this is very helpful.