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;

7 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
5 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.

6 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.

3 Likes

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.

1 Like

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.

1 Like

I look forward to both Math.mod and this operator!
Also I think we should definitely pair an integer division operator along with the modulo operator for the sake of completeness of Euclidean division.

How likely will this proposal be presented in a meeting?

I am interested in eventually championing a standard Math.mod function, but I probably would not have the bandwidth to do so until after at least BigInt Math resolves.

(I am not strongly against a %% operator, but I do think that the rest of the Committee is unlikely to ever standardize the operator, because it considers new syntax to be Very Expensive compared to standard functions; many Committee members would ask, “This operation is relatively rare. Why not just make this a function?”)

3 Likes

At least, I am willing to provide my package about music theory (currently work in progress) that use modulo quite frequently as a use-case example.

I'll note that Tab Atkins recently filed a couple issues while giving me a strong impression of support of the basic concept, and I saw Jordan Harband in the reactions to the issue that gave me that impression. So I wouldn't go as far as to say it's dead on arrival.

Also, it's worth noting there is quite a bit of language precedent, even for having multiple operators. (Wikipedia's table is extensive with over 100 languages listed, and they vary wildly with over a third having multiple types of modulo operations.)

Yeah, that @tabatkins and @ljharb are willing to engage about the operator is interesting—I’ll let them speak for themselves. Although I’m not against a %% operator, I still predict that it will get strong pushback from the Committee.

With regards to that table of precedents, I will point out that, of the 39 languages it lists that have at least two types of remainder/modulo built in, 31 of them support only one as an operator and the other is a function. Only 8 languages in the table provide two binary syntactic operators.

Truncated remainder Floored modulo Euclidean remainder Rounded remainder
Ada Operator Operator
CoffeeScript Operator Operator
Haskell Operator Operator
Modula-2 Operator Operator
Prolog Operator Operator
Seed7 Operator Operator
SenseTalk Operator Operator
VHDL Operator Operator
C Function Operator
C++ Function Operator
Clojure Function Function
Common Lisp Function Function
Dart Function Operator
Elixir Function Function
Elm Function Function
Erlang Operator Function
Euphoria Function Function
Forth Function Function
Fortran Function Function
GLSL None actually standardized Function
GDScript Operator Function
Go Operator Function
Java Operator Function
Julia Operator Function
Kotlin Operator Function
Logo Function Function
Lua Function Operator
Maple Operator by default Function by default
MATLAB Function Function
Perl Function Operator
Pure Data Operator Uncertain
Python Function Operator
Racket Function Function
Rust Operator Function
Scheme (R6RS) Function Function
Scheme (R7RS) Function Function
Smalltalk Ordinary message “Operator” message
Standard ML Function since SML ’97 Operator
XBase++ Operator Function

(Maple provides a single operator that can be configured, so I counted it as a “language with one mod/rem operator”. I was not able to confirm that Maxima’s remainder function is actually truncating, since the only remainder function I can find works on polynomial formulae, not numbers—but both Maxima’s mod and its remainder are functions anyway.)

I think an operator is a very hard sell before adding a Math.mod-style API function, and then seeing how much it's used. % is used so rarely that I'd be more likely to argue it probably shouldn't have been an operator in the first place, rather than that we should have two such operators.

I don't have an opinion on operator vs Math method, but having a proper modulo, rather than remainder, built-in in some way is a pretty valuable thing, I think.

Importantly, any comparison of languages needs to consider CSS when possible (since it's the other major web language), which now has both operators supported as the mod() (proper modulo, with sign matching the divisor) and rem() (like JS %, with sign matching the dividend) functions.

If this ends up just being a Math.rem() (matching the % op) and Math.mod(), that would be acceptable to me too. But the %% precedent does exist to lean on.

1 Like