Alec Gorge
Alec Gorge

Reputation: 17390

JavaScript % (modulo) gives a negative result for negative numbers

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

According to JavaScript, it is -13.

console.log(-13 % 64);

How do I fix this?

Upvotes: 392

Views: 146148

Answers (15)

Phume
Phume

Reputation: 67

Spotted. Fixed by using ternary operator:

var x = number % 12;
x = (x < 0)?(12+x):x;

Upvotes: 0

RARE Kpop Manifesto
RARE Kpop Manifesto

Reputation: 2809

Assuming the language you're working with uses truncated division algorithm, this function will simultaneously return the modulo values for truncated division, floored division, and also Euclidean division (plus safe handling of division by zero).

The main advantage of this function is that only one single division is performed to obtain all 3 values, thus avoiding the double-work approach suggested by Mozilla MDN :

((n % d) + d) % d

(Arguments are auto integer truncated, so a divisor of -0.31 has same effect as division by zero)

function triple_mod(___, __, _, ____) {

    return sprintf("T:%+d_F:%+d_E:%+d", _ = (____ = (__ = int(__)) == (_ = \
                   !!__) || __ == -_ || (___ = int(___)) ==  __ || !___ ||
                   ! (_ = ___ % __)) ? (__ ? _ < _ : (_ = log(_)) - _) : _,
                   ___ = (____ || (!__ < __) - (___ < !__)) ? _ : _ + __,
                         (____ ||  !_ < _) ? _ : _ < +___ ? ___ : _ - __)
}

+9007199254738183 +61277761 T:+38898571_F:+38898571_E:+38898571
+9007199254738183 -61277761 T:+38898571_F:-22379190_E:+38898571

-9007199254738183 +61277761 T:-38898571_F:+22379190_E:+22379190
-9007199254738183 -61277761 T:-38898571_F:-38898571_E:+22379190

+4688888899996789   +131071 T:+80187_F:+80187_E:+80187
+4688888899996789   -131071 T:+80187_F:-50884_E:+80187

-4688888899996789   +131071 T:-80187_F:+50884_E:+50884
-4688888899996789   -131071 T:-80187_F:-80187_E:+50884

The function has no declared types because it's truly polymorphic - it accepts integers, floats, and even ASCII numeric strings.

The function contains no hardcoded numbers at all since all the necessary constants and thresholds are derived on the fly. Rapid handling logic exists for matching inputs, zero-dividend, divisor of ±1, and remainder-less exact division.

All calls to abs(), ceil(), or floor() type functions have been eliminated since the emulated value for Euclidean div benefits from floored-div performing most of the heavy lifting on its behalf.

The call to natural log function "(_ = log(_)) - _" is for obtaining a proper IEEE-754 NaN derived from log(0) - log(0) := (-inf) - (-inf)

So while this function isn't exactly Javascript per se, it's generic enough it should easily be portable to any language.

Upvotes: 0

StuR
StuR

Reputation: 12218

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: https://jsperf.app/negative-modulo/2

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

Upvotes: 261

Enrique
Enrique

Reputation: 10117

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

Taken from this article: The JavaScript Modulo Bug

Upvotes: 387

zessx
zessx

Reputation: 68790

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

Upvotes: 4

Roko C. Buljan
Roko C. Buljan

Reputation: 205969

Fix negative modulo (reminder operator %)

Simplified using ES6 Arrow function, and without dangerously extending the Number prototype

const mod = (n, m) => (n % m + m) % m;

console.log(mod(-90, 360));    //  270  (Instead of -90)

Upvotes: 11

V. Rubinetti
V. Rubinetti

Reputation: 1696

For fun, here's a "wrap" function that works sorta like a modulo, except you can also specify the minimum value of the range (instead of it being 0):

const wrap = (value = 0, min = 0, max = 10) =>
  ((((value - min) % (max - min)) + (max - min)) % (max - min)) + min;

Basically just takes the true modulo formula, offsets it such that min ends up at 0, then adds min back in after.

Useful if you have a value that you want to keep between two values.

Upvotes: 2

Shanimal
Shanimal

Reputation: 11718

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

Upvotes: 23

quasimodo
quasimodo

Reputation: 3100

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 

Upvotes: 8

maartenpaauw
maartenpaauw

Reputation: 624

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

Upvotes: 1

bormat
bormat

Reputation: 1379

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); 
    }

Upvotes: 1

JayCrossler
JayCrossler

Reputation: 2129

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;
}

Upvotes: 2

wisbucky
wisbucky

Reputation: 37797

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

Upvotes: 16

Rob Sobers
Rob Sobers

Reputation: 21125

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

Upvotes: 41

dheerosaur
dheerosaur

Reputation: 15172

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.

Upvotes: 5

Related Questions