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

Posted

技术标签:

【中文标题】JavaScript % (modulo) 给出负数的负数结果【英文标题】:JavaScript % (modulo) gives a negative result for negative numbers 【发布时间】:2011-05-26 22:27:37 【问题描述】:

根据Google Calculator(-13) % 6451

根据 javascript(参见 JSBin),它是 -13

我该如何解决这个问题?

【问题讨论】:

基本上是 How does java do modulus calculations with negative numbers? 的副本,尽管这是一个 javascript 问题。 Javascript 有时感觉就像一个非常残酷的笑话 google 不会错 根本问题在于 JS % 不是模运算符。它是余数运算符。 JavaScript 中没有模运算符。所以接受的答案是要走的路。 为什么几乎没有语言实现模,考虑到它有多么有用? 【参考方案1】:
Number.prototype.mod = function (n) 
  return ((this % n) + n) % n;
;

摘自这篇文章:The JavaScript Modulo Bug

【讨论】:

我不知道我会称之为“错误”。模运算对负数的定义不是很好,不同的计算环境处理它的方式也不同。***在modulo operation 上的文章很好地涵盖了它。 它可能看起来很愚蠢,因为它通常被称为“模”,这表明它的行为与其数学定义相同(参见 ℤ/nℤ 代数),但事实并非如此。 为什么在加n之前取模?为什么不直接加 n 然后取模? @starwed 如果你不使用这个%n 它将失败 x < -n - 例如(-7 + 5) % 5 === -2((-7 % 5) + 5) % 5 == 3. 我建议添加到答案中,要访问此功能,应该使用格式 (-13).mod(10) 而不是 -13 % 10。这样会更清楚。【参考方案2】:

一个“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

【讨论】:

MDN 不是官方语言参考,它是一个社区编辑的网站,有时会出错。 The spec 不称它为模运算符,据我所知,它从来没有(我回到 ES3)。它明确表示该运算符产生隐含除法的余数,并称其为“% 运算符”。 糟糕,您指定的链接实际上引用了网址中的#sec-applying-the-mod-operator :) 无论如何,感谢您的注意,我从答案中剔除了绒毛,反正这并不重要。跨度> @沙尼马尔:哈哈!确实如此。 html 编辑器出错。规范文本没有。【参考方案3】:

使用Number.prototype 很慢,因为每次使用原型方法时,您的号码都会被包裹在Object 中。而不是这个:

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

用途:

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

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

比使用原型快 97%。如果性能对您当然很重要..

【讨论】:

很棒的提示。我拿了你的 jsperf 并与这个问题中的其他解决方案进行了比较(但无论如何这似乎是最好的):jsperf.com/negative-modulo/3 微优化。为此,您必须进行 大量 量的 mod 计算才能有所作为。编写最清晰和最可维护的代码,然后优化以下性能分析。 我认为在第二个示例 @StuR 中,您的 ns 和 ms 使用错误。应该是return ((n % m) + m) % m; 这个答案中陈述的动机是微优化,是的,但是修改原型是有问题的。更喜欢副作用最少的方法,就是这个。 @JeneralJames 更改原型的主要问题是命名空间冲突。归根结底,这只是全球数据的突变。在小的一次性代码之外,改变全局变量是不好的做法。将函数导出为可跟踪的依赖项。作为规则的例外,Polyfill 在这里是无关紧要的。这不是 polyfill。真正的 polyfill 遵循使碰撞安全的标准。如果您想在原则上对此进行辩论,则有一个单独的问题。 ***.com/questions/6223449/…【参考方案4】:

这不是错误,有 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

【讨论】:

在欧几里得函数中检查 m @bormat 是的,但是在 Javascript 中% 可以返回否定结果(这是这些函数的目的,修复它) 你写了这个 [code] Number.prototype.mod = function(n) var m = ((this%n)+n)%n;返回 m 如果没有这个检查,parseInt(-41).mod(-7) 将返回-6 而不是1(这正是我写的 Integer 部分函数的目的) 您可以通过删除第二个模数来简化您的函数 Number.prototype.mod = function(n) var m = this%n;返回 (m 【参考方案5】:

如果x 是整数且n 是2 的幂,则可以使用x &amp; (n - 1) 代替x % n

> -13 & (64 - 1)
51 

【讨论】:

【参考方案6】:

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

npm install just-modulo --save

从自述文件中复制的用法

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

【讨论】:

【参考方案7】:

我也处理负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); 
    

【讨论】:

【参考方案8】:

因此,如果您尝试调整度数(如果您有 -50 度 - 200 度),您可能希望使用类似:

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

【讨论】:

【参考方案9】:

接受的答案让我有点紧张,因为它重复使用了 % 运算符。如果 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

【讨论】:

如果 javascript 更改了模运算符以匹配数学定义,则接受的答案仍然有效。 “如果 Javascript 将来改变行为怎么办?” - 为什么会这样?改变这种基本操作员的行为是不可能的。 +1 分享这一关注和替代特色答案#answer-4467559 并出于 4 原因:(1) 为什么它说明,& 是“改变这种基本操作的行为不太可能”,但即使发现不需要它,也要谨慎考虑。 (2)根据损坏的操作定义一个工作操作,虽然令人印象深刻,但至少在第一次看时是令人担忧的,应该直到不显示(3)虽然我没有很好地验证这个替代方案,但我发现更容易遵循快速查看。 (4)tiny:它使用 1 div+1 mul 而不是 2 (mod) div&我听说过很多早期的硬件没有一个好的 FPU,乘法更快。 @DestinyArchitect 这不谨慎,毫无意义。如果他们要改变余数运算符的行为,就会破坏使用它的大量程序。这永远不会发生。 如果-*/;.(),Math.floor的行为怎么办987654331@ 或 return 更改?那么你的代码就被严重破坏了。【参考方案10】:

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

-1 % 8 // -1, not 7

【讨论】:

应该被称为余数运算符,但它称为模数运算符:developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/… @DaveKennedy:MDN 不是官方语言参考,它是一个社区编辑的网站,有时会出错。 The spec 不称它为模运算符,据我所知,它从来没有(我回到 ES3)。它明确表示该运算符产生隐含除法的其余部分,并称其为“% 运算符”。 如果调用remainder,则定义必须大于0。你还记得高中的除法定理吗?!所以也许你可以看看这里:en.wikipedia.org/wiki/Euclidean_division @Ahmad——现在称为multiplicative operator。 "mod" 从一开始就应该在每种语言中实现。经过 30 年的编程,当 a 为负数时,我——从不——需要 a % b:每次,我需要的是 mod(a,b)。【参考方案11】:

虽然它的行为不像您预期​​的那样,但这并不意味着 JavaScript 没有“行为”。这是 JavaScript 为其模计算所做的选择。因为,根据定义,任何一个答案都是有意义的。

参见来自***的this。您可以在右侧看到不同的语言如何选择结果的符号。

【讨论】:

以上是关于JavaScript % (modulo) 给出负数的负数结果的主要内容,如果未能解决你的问题,请参考以下文章

C - Raising Modulo Numbers (POJ - 1995)

CodeForces - 1553F Pairwise Modulo(数论+树状数组)

Flutter geolocator 包在 IOS 应用程序上给出负纬度,在 Android 上给出正确坐标

c ++ chrono给出负纪元时间

交叉验证给出负 R2?

向量中的累加函数(STL)给出负和