JavaScript % (modulo)给出负数的负结果

根据谷歌计算器(-13) % 6451

根据Javascript(参见这个JSBin),它是-13

我怎么解决这个问题?

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

选自本文:JavaScript模数错误

虽然它没有像你期望的那样运行,但这并不意味着JavaScript没有“运行”。这是JavaScript为模数计算所做的选择。因为根据定义,两个答案都有意义。

参见维基百科中的。您可以在右边看到不同的语言如何选择结果的符号。

%运算符在JavaScript中是余数运算符,而不是模运算符(主要区别在于负数的处理方式):

-1 % 8 // -1, not 7

一个“mod”函数,返回一个正的结果。

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

当然

mod(-13,64) // 51

接受的答案让我有点紧张,因为它重用了%操作符。如果Javascript在未来改变了行为呢?

下面是一个不重用%的解决方案:

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

使用Number.prototype是很慢的,因为每次使用原型方法时,你的数字都被包装在Object中。而不是这样:

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

使用:

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

看:https://jsperf.app/negative-modulo/2

比使用原型快97%。当然,如果业绩对你来说很重要的话。

这不是一个错误,有3个函数来计算模,你可以使用一个适合你的需要(我建议使用欧几里得函数)

截断小数部分函数

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

整部函数

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

欧几里得函数

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

所以看起来如果你想要mod周围的度(所以如果你有-50度- 200度),你会想要使用这样的东西:

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

如果x是一个整数,而n是2的幂,你可以使用x & (n - 1)代替x % n

> -13 & (64 - 1)
51

我还要处理négative a和- n

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

有一个NPM包可以帮你完成这项工作。您可以使用以下命令安装它。

npm install just-modulo --save

从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存储库可以通过以下链接找到:

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

为了好玩,这里有一个“总结”;函数,工作方式有点像模数,除了你也可以指定范围的最小值(而不是0):

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

基本上只需要取真模公式,对其进行偏移,使min最终为0,然后在后面添加min

如果有一个值希望保持在两个值之间,则很有用。

修正负模(提醒操作符%)

简化使用ES6箭头功能,没有危险的扩展数字原型

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


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