Modulo Operator %%

*copied from es-discuss mailing list

JS needs a modulo operator. It currently has the remainder operator % which works in most cases except for negative values. I believe %% would work great and be easy to remember.

let x = (-13) %% 64;

is equivalent to

let x = ((-13 % 64) + 64) % 64;

4 Likes

Is this a request for a proper modulus operator instead of just the builtin remainder operator?

Would a Math.mod function suffice? That would avoid the need for new syntax and could be polyfilled via, IIRC

Math.mod = (a, b) => ((a % b) + b) % b
3 Likes

See also:

2 Likes

Yes, Math.mod would work, but I believe %% would be a nice addition similar to how we have Math.pow and **, the exponentiation operator.

3 Likes

I feel that this proposal should be urgently considered. A very long contribution has shown that almost all languages provide two operators, one giving the remainder with the same sign as the dividend, the other giving the modulus, with the same sign as the divisor. JS misses the latter, i.e. a %% b of same sign as b.

Because JS misses a modulo operator, several calendar ICU were bugged for many years. They were fixed only with ICU 68.

I love the proposal of %% operator, although Math.mod would already be a big step forward.

As for the algorithm, I think wise to use add or subtract divisor from dividend, until dividend is in the range 0..divisor (or divisor..0 if divisor < 0). This way you do not loose precision.

Searching for someone to bring the proposal ? I can do it.

Yes if you are willing!

As for the algorithm, I think wise to use add or subtract divisor from dividend, until dividend is in the range 0..divisor (or divisor..0 if divisor < 0). This way you do not loose precision.

First, just to be clear, JS, indirectly due to IEEE-754, defines its remainder operation using truncated division (r = a - b*trunc(a/b)), thus it has the same sign as the dividend (the a above).

In my proposed spec, I want the result to keep the sign of the divisor. So I'd use the equation r = a - b*floor(a/b) instead, giving r the same sign as b.

So far, this all seems irrelevant (aside from countering your confusion of divisor/dividend) and simple. But we have a shortcut courtesy of IEEE-754, one that I wish JS exposed: copysign(a, b). (Its implementation is an extremely simple bitwise operation: (double) ((uint64_t) a & ~(1ULL << 63) | (uint64_t) b & (1ULL << 63)) - I'm almost surprised it's not implemented in hardware as it doesn't even require gates, just bridges.) If we don't like the sign we get out of fmod, we can just set it to whatever we want with copysign, so we could just use copysign(fmod(a, b), b) for a %% b and get exactly the precision we want (as full as fmod allows), just rounded to zero.

Sorry, I still cannot see where a made a confusion.

Yes, r such as r = a - b*floor(a/b) is the expected result for the modulo operator.
But per your proposed spec , you take the absolute value of the remainder, and set the result to the Number having the same absolute value and the sign of b, the divisor. This will not give the expected result.
Let's take e.g. a = -5 and b = 3.
The remainder -5 % 3 is -2, same sign as a, but as b is positive, result is 2 per your spec.
However -5 modulo 3 is 1. You can check that -5 -3*floor (-5/3) is -5 -3 * (-2) i.e. -5 + 6.
So using copysign(fmod(a, b), b) for a %% b is not the solution. Would have been nice !

Using ((a % b ) + b ) % b as MDN suggests works also, but this expression is anything but familiar.

As read in the comments, many developers (including myself) write their own modulo function. Let's give them a nice feature. No doubt we will find the best algorithm for it.

Ah, I see where you're coming from. File a bug against my repo then, and I'll look into a better fix. (I'm trying to avoid multiple fmod operations, so my solution may still differ.)

I've updated my proposal to 1. fix the bug with my spec (copysign is the wrong way to do it) and 2. redefine it as always-positive rather than divisor-dependent.

That way it's much easier to use and generally more intuitive.

Hello,
it seems, there is some issue with the current proposal:
4 %% -3 === 7 according to the current document.
I think, It should be q = sign(d) * floor(n / abs(d)) .
The same defintion is used at Modulo operation - Wikipedia (but variable names are different)

Btw, in Chrome a % b is slower than a - Math.trunc(a / b) * b when working with "safe integers"...

The proposal is intended to use Euclidean division, and I tried to spec it in terms of that, using the current spec for remainder (using truncated division) as a basis for it. Feel free to file a pull request with something more correct, and I'll merge it.