【问题标题】:JavaScript % (modulo) gives a negative result for negative numbersJavaScript % (modulo) 给出负数的负数结果
【发布时间】: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 中没有模运算符。所以接受的答案是要走的路。
  • 为什么几乎没有语言实现模,考虑到它有多么有用?

标签: javascript math modulo


【解决方案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 遵循使碰撞安全的标准。如果您想在原则上对此进行辩论,则有一个单独的问题。 stackoverflow.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。您可以在右侧看到不同的语言如何选择结果的符号。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-07-23
            • 2019-04-30
            • 1970-01-01
            • 1970-01-01
            • 2021-06-11
            • 2021-06-09
            相关资源
            最近更新 更多