JavaScript % (modulo) gives a negative result for

2019-01-01 03:58发布

问题:

According to Google Calculator (-13) % 64 is 51.

According to Javascript (see this JSBin) it is -13.

How do I fix this?

回答1:

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

Taken from this article: The JavaScript Modulo Bug



回答2:

Using Number.prototype is SLOW, because each time you use the prototype method your number is wrapped in an Object. Instead of this:

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

Use:

function mod(n, m) {
  return ((n % m) + m) % m;
}

See: http://jsperf.com/negative-modulo/2

~97% faster than using prototype. If performance is of importance to you of course..



回答3:

The % operator in JavaScript is the remainder operator, not the modulo operator (the main difference being in how negative numbers are treated):

-1 % 8 // -1, not 7



回答4:

A \"mod\" function to return a positive result.

var mod = function (n, m) {
    var remain = n % m;
    return Math.floor(remain >= 0 ? remain : remain + m);
};
mod(5,22)   // 5
mod(25,22)  // 3
mod(-1,22)  // 21
mod(-2,22)  // 20
mod(0,22)   // 0
mod(-1,22)  // 21
mod(-21,22) // 1

And of course

mod(-13,64) // 51


回答5:

The accepted answer makes me a little nervous because it re-uses the % operator. What if Javascript changes the behavior in the future?

Here is a workaround that does not re-use %:

function mod(a, n) {
    return a - (n * Math.floor(a/n));
}

mod(1,64); // 1
mod(63,64); // 63
mod(64,64); // 0
mod(65,64); // 1
mod(0,64); // 0
mod(-1,64); // 63
mod(-13,64); // 51
mod(-63,64); // 1
mod(-64,64); // 0
mod(-65,64); // 63


回答6:

JavaScript Modulo operation

Successful implementation of a scientific calculation or algorithm is possible not only by understanding the features that a particular language or framework offers but also understanding the limitations.

Computers are precise scientific instruments but they do they work by manipulating entities in discrete spaces (you have a limited number of pixels on the screen, there is a limited numbers of bits that are behind each number, etc.)

Try to ignore the limitations or framework specs and soon you’ll find out that you have an impedance mismatch between your mathematical formula and the code you try to write.

Modulo operator

Sometimes the situations is complicated by falsely advertised or understood framework functions or operators. This article focuses on the modulo operator.

Ask any C# or JavaScript programmer what is the modulo operator in their language and there is a big chance that they’ll gone answer: % (e.g. the percentage sign). Plenty of documentation refer to the % sign as modulo operator.

Wow! This is a subtle but very dangerous mistake. In C# and JavaScript % operator is used actually to calculate the remainder (with sign) left over when one operand is divided by the second operand. Therefore the operand should be correctly referred to as the signed remainder operator.

At first sight the signed remainder operator functions similarly to the modulo operator. Let’s do some tests by comparing the results returned by JavaScript with the ones returned by Google.

In Chrome, open the console (press F12 and select the Console tab). Type there, one by one, the calculations from the left column. Next type the same expressions in the Google search bar. Notice the results. They should be the same.

                JavaScript  Google
    5 % 3       2           2
    26 % 26     0           0
    15 % 12     3           3

Let’s now try to use a negative value as the first operand:

\"enter

Surprise!

-5 % 3 = 1 (according to Google) -5 % 3 = -2 (according to JavaScript)

Well … this shouldn’t be actually a surprise if we look at the definition of % operator in JavaScript (… or even C# or many other languages). Google calculates the true modulo, while these computer languages calculate a signed reminder.

However, not all programming languages / frameworks have the same implementation for %. In Python, for instance, the % operator calculates the true modulo in the same way as Google.

\"enter

This difference in behavior between languages may introduce subtle errors in your calculation, especially if you are trying to port an algorithm from one language to another!

A problem understood is a problem half solved

Let’s suppose we need to implement a (scientific) calculation in JavaScript by using modulo arithmetic.

Since we now understand that JavaScript doesn’t have a true modulo operator, we can easily implement our modulo operation as a function.

There are multiple ways to implement modulo in JavaScript. I’ll show you 3 ways of doing it.

// Implement modulo by replacing the negative operand 
// with an equivalent positive operand that has the same wrap-around effect
function mod(n, p)
{
    if ( n < 0 )
        n = p - Math.abs(n) % p;

    return n % p;
}

// Implement modulo by relying on the fact that the negative remainder
// is always p numbers away from a positive reminder
// Ex: -5 % 3 | -5 = -2 * 3 + 1 and -5 = -1 * 3 + (-2) | -2 + 3 = 1  
function mod(n, p)
{
    var r = n % p;

    return r < 0 ? r + p : r;
}

// Implement modulo by solving n = v * p + r equation  
function mod(n, p) 
{
    return n - p * Math.floor( n / p );
}

With more precise tools at our disposal, we are now ready to tackle that (scientific) calculation and expect to get correct results each and every time.

Note: There are plenty of calculations that make use of modulo arithmetic… If you want to see how to use these new modulo functions in implementation of a Caesar Cipher / ROT13 code, you can check this article.



回答7:

Though it isn\'t behaving as you expected, it doesn\'t mean that JavaScript is not \'behaving\'. It is a choice JavaScript made for its modulo calculation. Because, by definition either answer makes sense.

See this from Wikipedia. You can see on the right how different languages chose the result\'s sign.



回答8:

If x is an integer and n is a power of 2, you can use x & (n - 1) instead of x % n.

> -13 & (64 - 1)
51 


回答9:

So it seems that if you\'re trying to mod around degrees (so that if you have -50 degrees - 200 degrees), you\'d want to use something like:

function modrad(m) {
    return ((((180+m) % 360) + 360) % 360)-180;
}


回答10:

I deal with négative a and negative n too

 //best perf, hard to read
   function modul3(a,n){
        r = a/n | 0 ;
        if(a < 0){ 
            r += n < 0 ? 1 : -1
        }
        return a - n * r 
    }
    // shorter code
    function modul(a,n){
        return  a%n + (a < 0 && Math.abs(n)); 
    }

    //beetween perf and small code
    function modul(a,n){
        return a - n * Math[n > 0 ? \'floor\' : \'ceil\'](a/n); 
    }


回答11:

This is not a bug, there\'s 3 functions to calculate modulo, you can use the one which fit your needs (I would recommend to use Euclidean function)

Truncating the decimal part function

console.log(  41 %  7 ); //  6
console.log( -41 %  7 ); // -6
console.log( -41 % -7 ); // -6
console.log(  41 % -7 ); //  6

Integer part function

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

console.log( parseInt( 41).mod( 7) ); //  6
console.log( parseInt(-41).mod( 7) ); //  1
console.log( parseInt(-41).mod(-7) ); // -6
console.log( parseInt( 41).mod(-7) ); // -1

Euclidean function

Number.prototype.mod = function(n) {
    var m = ((this%n)+n)%n;
    return m < 0 ? m + Math.abs(n) : m;
};

console.log( parseInt( 41).mod( 7) ); // 6
console.log( parseInt(-41).mod( 7) ); // 1
console.log( parseInt(-41).mod(-7) ); // 1
console.log( parseInt( 41).mod(-7) ); // 6


回答12:

There is a NPM package that will do the work for you. You can install it with the following command.

npm install just-modulo --save

Usage copied from the README

import modulo from \'just-modulo\';

modulo(7, 5); // 2
modulo(17, 23); // 17
modulo(16.2, 3.8); // 17
modulo(5.8, 3.4); //2.4
modulo(4, 0); // 4
modulo(-7, 5); // 3
modulo(-2, 15); // 13
modulo(-5.8, 3.4); // 1
modulo(12, -1); // NaN
modulo(-3, -8); // NaN
modulo(12, \'apple\'); // NaN
modulo(\'bee\', 9); // NaN
modulo(null, undefined); // NaN

GitHub repository can be found via the following link:

https://github.com/angus-c/just/tree/master/packages/number-modulo