I run into this issue in javascript all the time. I want a number to basically act like pacman leaving the left or right side of a screen. pure sawtooth function. unfortunately js’s % operator mirrors at zero. so i always end up having to search for this:
Agreed. In my experience, people want to use this modulo operator (e.g. for implementing a ring buffer or wrap-around as you mentioned), but many languages use the other modulo which leads to all sorts of bugs. It doesn’t make sense to me since this modulo is more natural from both a mathematical and an engineering perspective.
function modn(a,b){ return a-Math.round(a/b)*b }
// this returns in the cycle -0.5 to 0.5
function modp(a,b){ return a-Math.floor(a/b)*b }
// this returns 0 to 1 (times the cycle `b`)
They can actually run faster than the % operator for some reason, although not if put on Numbers prototype which is still quite slow on latest engines.
I ran a quick check on possible rounding differences with % - compared results of a few million very large random number inputs:
For positive Integers modp(a,b) returns identically to a%b
For positive reals 8% of returns differ by about 1 ulp.
at very large or very small scales, you expect some level of innaccuracy for any IEEE float op. acceptable level of error always depends on what the program is aiming to do I suppose.
Haskell supports both. The documentation explains the two pretty clearly, IMO:
quot :: a -> a -> a
integer division truncated toward zero
rem :: a -> a -> a
integer remainder, satisfying
(x `quot` y)*y + (x `rem` y) == x
div :: a -> a -> a
integer division truncated toward negative infinity
mod :: a -> a -> a
integer modulus, satisfying
(x `div` y)*y + (x `mod` y) == x
I find true module to be an insanely useful operator. Python negative indexing (array[-1]) is essentially that. I’ve proposed it to swift but people didn’t see the point https://forums.swift.org/t/double-modulo-operator/2718
Python negative indexing isn't modulo. It's just shorthand for array[len(array)-i].
>>> a = [1, 2, 3]
>>> a[-1]
3
>>> a[-2]
2
>>> a[-3]
1
>>> a[-4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range