-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mod/intdiv Mathematical Operators to Work Around JavaScript Oddities #1971
Comments
+1 for //, although I believe it's been proposed before. I like it in Python. Is there a reason to prefer to parseInt to Math.floor? +0.1 for %%. I've been bitten by JS's strange modulo behavior, but I think the workaround is idiomatic enough that it might not justify new syntax. I don't have any strong objections. |
Should totally be Math.floor. |
Sample compilation:
|
The advantage that CoffeeScript's compilation support has is that we could avoid the function call by compiling the modulo to this:
->
Advantages: branch free, no function call involved for the actual modulo. |
Yep, except that's not very readable. We could inline all of the helpers we have, but we don't because it wouldn't be as readable or as DRY. |
For a fixed modulo I often choose to extend Number.prototype:
That way I can do
I do the same with Math.floor as well:
New operators are a cool idea, but adding syntax has big hidden costs. I just want to illustrate another way to do it. |
Aside from the fact that patching prototype is literally the worst way to make this work, I understand that a helper function can be used as a replacement. That's in fact what I do currently. Just pointing out that it's so very common a operation that deserves an operator (or two in this case). |
I searched some code that I wrote, and Math.floor shows up in the same line as division once every 500 lines or so, and a quick inspection suggests they're almost all candidates for '//'. It's often enough to be annoying, but maybe not worth new syntax. Searching for %% use cases is a little more difficult, since the negative number gotcha depends on the context, but a rough count shows about the same frequency. |
The problem is that many people work around the stupid mod operator in JavaScript by hand now instead of using the modulo operator. Wrapping around on left and right is a very common thing to do. I use modulo all the time in Python but I stopped using that in JavaScript and instead went to if conditions since the step size is most of the time 1. The availability of such an operator would make people actually use mod more :-) |
@mitsuhiko Why is patching Number.prototype the worst way? What's so bad about it? |
Impossible to properly isolate. Leads to possible namespace collisions. It's called monkeypatch for a reason :-) Seriously though. It gives you nothing over a function and it comes with downsides, so why use it. |
See #80. Jeremy rejected // and ** pretty early on. |
+1 for %% Been bitten too many times |
The first implementation mentioned, i.e.
works properly in most cases, and is definitely the simplest implementation, but it falls down in a few edge cases. I use the following in my "mathutils", which is somewhat slower but should always be correct:
|
Would love this. |
@jashkenas: I think it's about time for you to put the hammer down on this issue. Is CoffeeScript going to get modulo/intdiv operators? |
I think it's interesting, but should be rolled up as part of a comprehensive proposal to add the "complete" suite of new math operators -- including a power operator, and others we've discussed before. |
I came up with this implementation for the new modulo operator, lengthy but seems fastest:
Since noone mentioned it yet, I would like to point out that integer division, at least for me, is a composition of two operations: division and rounding. Even though the way it works is well established, because it is the first kind of division we learn in school, languages like Python ran into some trouble with including integer divison and adding new operator doesn't get rid of the ambiguity for newcomers (oh my, two divisions, which one should I use, let's hack). Not having to deal with integer division, learning how to get the correct value I want (rounding up/down/fairly) properly has certain value to it. Sure, it is a lot of typing but it is much more visible what I am doing when using Back to modulo, I actually wish this was the default % behaviour and I would never have to do again |
I don't know why, but sometimes i like falling in these seemingly superfluous performance comparisons. Thanks for the blog post @xixixao! =D I borrowed the mod implementations you posted on your blog and made this "correctness" test: runTests = (modFn, name) ->
t = (a, b, expected) ->
res = modFn a, b
if res isnt expected and not ((isNaN res) and (isNaN expected))
console.log "#{name} - error on #{a} %% #{b}. Expected #{expected}, got #{res}"
t 0, 1, 0
t 0, -1, 0
t 1, 0, NaN
t 1, 2, 1
t 1, -2, -1
t 1, 3, 1
t 2, 3, 2
t 3, 3, 0
t 4, 3, 1
t -1, 3, 2
t -2, 3, 1
t -3, 3, 0
t -4, 3, 2
t 5.5, 2.5, 0.5
t -5.5, 2.5, 2.0
fastMod = (n, base) ->
if (_result = n % base) < 0 && base > 0 || _result > 0 && base < 0
_result + base
else _result
originalMod = (n, base) ->
(n % base + base) % base
saferMod = (n, base) ->
unless (jsmod = n % base) and (n > 0 ^ base > 0) then jsmod
else jsmod + base
fasterSaferMod = (n, base) ->
unless (jsmod = n % base) and ((n > 0) != (base > 0)) then jsmod
else jsmod + base
runTests fastMod, 'Fast mod'
runTests originalMod, 'Original mod'
runTests saferMod, 'Safer mod'
runTests fasterSaferMod, 'Faster safer mod' All implementations seem to be equivalent and correct. Please let me know if i forgot some use-case. Also, the performance seems to depend a lot on which browser is used and whether the inputs are natural numbers, integers o floats. Take a look at this JS Perf. I've only tested it on Firefox and Chrome under Ubuntu. For integers and floats, the "fast" and "faster safer" implementations seems to perform better, but for natural numbers the "original" implementation seems to beat everyone on Firefox (i was kinda (positively) surprised to see Firefox out-perform Chrome this much TBH). Given that the performance seems to be so browser/data dependant, i'd suggest to go with the simplest and clearer implementation: the original |
Thanks @epidemian, I didn't know JS Perf and I am glad it matches my results (uff). I agree with the outcome, especially because of the look of the generated Javascript. |
Moving to #2887 |
One of the weird aspects of JavaScript is how it defines modulo:
In comparison Python/Ruby define them to always have the sign of the right side:
Likewise it's very common to have to divide and require an integer result. As such I would propose two new operators '%%' for right handside signed mod and '//' for integer division:
a %% b
would compile to
And
a // b
would compile to
When is this useful? For instance consider wanting to move a player on a grid with the intent of mirroring over on edges:
Another use case (where also the integer division comes in handy) is converting for instance things like global coordinates to locals:
The text was updated successfully, but these errors were encountered: