4

I have been conducting a few tests in Javascript, I will show them (pasted from console):

-1 % 4
-1
-4 % 4
-0
-8 % 4
-0
-6 % 4
-2
-6 % -4
-2
6 % -4
2

-1 % 4 is truly -1, however, I do not really understand why is Javascript not yielding 3, which is equally correct, but more canonic.

What is -0? I know -4 / 4 = -1, which is an integer, but I am not sure what -0 is.

I am puzzled about Javascript's calculation of modulo.

My tests were motivated by a slightly unpleasant surprise that I have been working on a gallery and I have an index for the active picture. There are two buttons, for next and previous buttons. The next image of the last image is the first image and the previous image of the first image is the last image. I have been changing the index as:

currentImageIndex = (currentImageIndex + 1) % images.length;

when the user clicked on the next button. When the user clicked on the previous button, I have been trying with the following code:

currentImageIndex = (currentImageIndex - 1) % images.length;

I was surprised to see that the latter did not work well, as the previous image was not shown and an error was thrown as an invalid index was used in the array indexed by currentImageIndex. I have console.log-ed the value and have seen that it is -1! Ok, I have worked around the problem with:

currentImageIndex = (currentImageIndex + images.length - 1) % images.length

and it was not too painful, but still, I did not understand the logic behind the result of the calculation. So, is there somebody, who kows how Javascript's modulo calculation works, as I am really puzzled and I see -0 as a result of -4 % 4 to be the joke of the month.

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • ( -0 === 0 && -0 == 0 ) === true, so why's it matter – Cory Danielson Jun 05 '14 at 00:49
  • 2
    But `(1/-0 === 1/0) is false`, so it does. – 000 Jun 05 '14 at 00:51
  • It is chaotic and it matters when modulo yields -1 instead of the expected 3 as the result of -1 % 4. It makes the difference of having to add another number into the calculation to force the result to be in canonic form. – Lajos Arpad Jun 05 '14 at 00:52
  • since when is dividing by any form of 0 a thing – Cory Danielson Jun 05 '14 at 00:53
  • It is not only a thing... It is infinity, my friend. – Lajos Arpad Jun 05 '14 at 00:54
  • 3
    possible duplicate of [Javascript modulo not behaving](http://stackoverflow.com/questions/4467539/javascript-modulo-not-behaving) – Tom Panning Jun 05 '14 at 00:54
  • @TomPanning, not a duplicate, as the referred question wants to fix the behavior and I want to understand it. So both questions are referring Javascript modulo calculation, but the questions are asking different things. – Lajos Arpad Jun 05 '14 at 00:55
  • 1
    It's explained in the [docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Arithmetic_operators), it's a binary operator that returns the integer remainder of dividing the two operands, so it's not a true euclidean modulo. – adeneo Jun 05 '14 at 00:56
  • 1
    Have you tried [*the spec*](http://ecma-international.org/ecma-262/5.1/#sec-11.5.3)? – RobG Jun 05 '14 at 00:57
  • @adeneo, then why is it called modulus in the link you referred me to? – Lajos Arpad Jun 05 '14 at 00:58
  • In javascript `%` (sadly mislabeled modulo in the spec) is actually the *remainder operator*!! There's a difference. – GitaarLAB Jun 05 '14 at 00:59
  • @GitaarLAB, then why is -4 % 4 === -0? Is that explained by the remainder/modulo difference? – Lajos Arpad Jun 05 '14 at 01:00
  • That's because in ECMAScript there is (well, in the future probably was) a difference between `-0` and `+0` – GitaarLAB Jun 05 '14 at 01:01
  • @LajosArpad - It is in fact a binary modulo operation, but it returns the remainder, the more commonly used (in math) [euclidean](http://en.wikipedia.org/wiki/Euclidean_algorithm) version returns the *greatest* common divisor, so it's a little different, but in most cases it doesn't really matter. – adeneo Jun 05 '14 at 01:02
  • @GitaarLAB, so -4 % 4 === -0 because there is a difference between -0 and +0? – Lajos Arpad Jun 05 '14 at 01:02
  • @RobG, that should be an answer, as the spec clearly describes the behavior. Please, create an answer and include the link so people in the future will understand the behavior. – Lajos Arpad Jun 05 '14 at 01:04
  • @adeneo: it matters for example when one emulates an analog compass where you'd expect a proper heading when you subtract 45 degrees from 10 degrees (that's how I know and understand the difference). @LajosArpad: as far as I know, yes, because it is remainder, not modulo. `4 % 4 === +0` (well without the +). – GitaarLAB Jun 05 '14 at 01:07
  • @LajosArpad—done that, but when I follow the algorithm in ECMA-262 I get `-1%4=3`, not `-1`, which is what browsers seem to return (though I haven't tested many). – RobG Jun 05 '14 at 01:35
  • @GitaarLAB - it *generally* doesn't matter, as when for instance doing a compass heading you would just do `10-45` and maybe some dividing with 360 and `Math.abs` etc, and not mix in the modulo at all. The same applies when using an index to access an array like in the OP's case, you would just `++` and `--` the index, and reset to one when it reaches the same length as the array etc. you generally don't keep adding on and using modulus to get an index like that. – adeneo Jun 05 '14 at 01:49
  • We have negative zero and positive zero in JS because that's how the IEEE 754 standard defines it, which is what JS uses. And it can be useful if you want to test whether you have a negative or positive change of a value (i.e. if a value is approaching 0 from the positive or negative side). – Felix Kling Jun 05 '14 at 02:09

4 Answers4

4

Great question!

It seems to me that ECMA is confusing and it's easy to miss how the modulo operator works (I certainly did). From ECMA-262 §11.5.3 there is a statement that:

The result of a floating-point remainder operation as computed by the % operator…

which infers a remainder operation. It goes on to provide an algorithm:

…where neither an infinity, nor a zero, nor NaN is involved, the floating-point remainder r from a dividend n and a divisor d is defined by the mathematical relation r = n − (d × q) where q is an integer that is negative only if n/d is negative and positive only if n/d is positive, and whose magnitude is as large as possible without exceeding the magnitude of the true mathematical quotient of n and d. r is computed and rounded to the nearest representable value using IEEE 754 round-to-nearest mode.

Applying that to the case of -1 % 4, then:

n = -1
d = 4
trueMathematicalQuotient = -1/4 = -0.25

Therefore q must be negative since n/d is negative. The largest negative integer that d (4) can be multiplied by that is less than or equal to -0.25 in magnitude and that the gives a result less than or equal to n (-1) is -0 (noting that -1 has greater magnitude than -0.25).

The simple way to do that is to truncate q to an integer:

q = -0 // -0.25 truncated

Putting numbers into the equation:

r = -1 - (4 * 0)
r = -1 - 0
r = -1

Which can be put in a function as:

function remainderMod(n, d) {
  var q = parseInt(n / d);  // truncates to lower magnitude
  return n - (d * q);
}

or abbreviated to:

function remainderMod(n, d) {
  return n - (d * (n/d | 0));
}
RobG
  • 142,382
  • 31
  • 172
  • 209
  • Two thoughts regarding `q`. The algorithm says *"the sign of the result equals the sign of the dividend."*. So it's clear that the result must be negative. It also says *"and whose magnitude is as large as possible without exceeding the magnitude of the true mathematical quotient of n and d"*. So maybe `-1` has a greater magnitude than `-0.25`? – Felix Kling Jun 05 '14 at 02:19
  • No, I think you are right with the truncating part. I just wanted to point out that the information (the magnitude) is in the algorithm (I believe). It refers to `q`, not `r`: *"q is an integer that is [...], and whose magnitude is as large as possible without exceeding the magnitude of the true mathematical quotient of n and d"*. But maybe that's just *part* of the overall explanation. The truncating division is certainly the more important part. – Felix Kling Jun 05 '14 at 02:49
  • @FelixKling—I think I got there in the end. – RobG Jun 05 '14 at 03:07
  • Very nice answer, too bad I can only accept an answer. I have already accepted Tom Panning's answer, which is also correct. However, I compensate with an upvote. – Lajos Arpad Jun 05 '14 at 21:48
2

The OP wanted to understand what is going on (not necessarily "fix" it). The short answer is that JavaScript has a "remainder" operator not a "modulo" operator (according to Douglas Crawford). For the full difference, here is a description of modulo vs remainder (specifically referring to C#).

Edit: Finally, the spec has the definitive answer.

Tom Panning
  • 4,613
  • 2
  • 26
  • 47
  • +1, nice answer, but if you include the link given by RobG, I will accept it, as that link describes the way Javascript works with the operator. It does not describe the cause (my main point of interest), but it describes the way it is calculated, which is the answer to the question. – Lajos Arpad Jun 05 '14 at 01:20
0

Have a look at this question, and this answer which has a solution for you.

The problem you're running into is that JavaScript's % operator is not a true modulo, but rather a simple remainder.

In the case that the above link breaks, here is the solution:

Number.prototype.mod = function(n) {
  return ((this%n)+n)%n;
}

This can be used like this:

(-1).mod(4); // returns 3

If you don't want to alter the prototype of Number you can instead just make a function:

function modulo(a, b) {
  return ((a % b) + b) % b
}

Which can be used like this:

modulo(-1, 4); // returns 3
Community
  • 1
  • 1
jshanley
  • 9,048
  • 2
  • 37
  • 44
  • No, if you read my question again, you will see that I have solved the problem. I asked the question to understand the behavior, as I have already solved the bug. If you look at the link given by RobG, you will see the cause, so the question is already answered, but I am still waiting for him to answer the question so I can accept the answer. – Lajos Arpad Jun 05 '14 at 01:08
  • @LajosArpad you said in your question: "I do not really understand why is Javascript not yielding 3, which is equally correct, but more canonic." – jshanley Jun 05 '14 at 01:12
  • But this is searching for the cause of a default behavior, not for a solution. – Lajos Arpad Jun 05 '14 at 01:18
  • @LajosArpad alright, I'm not looking to have an argument over this... I included a solution because others might come here looking for help with the same issue as well. I *did* mention that the reason was because the `%` is not a true modulo, and linked to a relevant discussion about that. I hope you found the info you were after. – jshanley Jun 05 '14 at 01:22
0

Modulo Function is a remainder. So 10 % 4 means "Divide 10 by 4 and return the remainder" When the remainder is 0 you know that that it is perfectly divisible. So -6 % 4 = -4. This happens because Javascript sees the -6 as a non existant number, or another type of 0, so you are left with that negative number after it divides it by the whole number 0 times. It is is known bug.