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

Ale*_*rge 224 javascript math modulo

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

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

我该如何解决?

Enr*_*que 234

Number.prototype.mod = function(n) {
    return ((this%n)+n)%n;
};
Run Code Online (Sandbox Code Playgroud)

摘自本文:JavaScript Modulo Bug

  • 我不知道我会称之为"虫子".模数运算在负数上没有很好地定义,并且不同的计算环境以不同的方式处理它.维基百科关于[模运算]的文章(http://en.wikipedia.org/wiki/Modulo_operation)很好地涵盖了它. (22认同)
  • 它可能看起来很愚蠢,因为它通常被称为"模数",表明它的行为与它的数学定义相同(参见ℤ/nℤ代数),但事实并非如此. (19认同)
  • @starwed如果你没有使用这个%n它会失败'x <-n` - 例如`(-7 + 5)%5 === -2`但是`(( - 7%5)+ 5) %5 == 3`. (11认同)
  • 我建议添加一个访问此功能的答案,应该使用格式(-13).mod(10)而不是-13%10.这将更清楚. (7认同)
  • 为什么在添加n之前采用模数?为什么不添加n然后取模数? (6认同)
  • @etienne 不正确。在数学中,绝对正确,例如 -1 % 12 = -1。那是因为在 mod 12 代数中,-1 = 11。它们是指代相同等价类的不同符号。 (3认同)
  • @NoBugs - 没有人说它是默认定义的.这个答案中的代码是定义它的.运行该代码后,您可以说`(-13).mod(64)`并获得`51`.http://jsfiddle.net/63KyC/ (2认同)
  • 我看到,从这篇文章中我暗示 .mod 覆盖了 % mod 行为 - 如果是这样,该函数将首先是一个本机方法。所以这个方法实际上与 `Number.prototype.myfunction = ...` 没有什么不同 (2认同)
  • JavaScript 计算不同于模的余数 (2认同)
  • 另请注意,“%”现在称为 [*MultiplicativeOperator*](http://ecma-international.org/ecma-262/8.0/#prod-MultiplicativeOperator),并与 C 和 C++ 余数运算符进行比较。 (2认同)

Stu*_*tuR 138

使用Number.prototype是SLOW,因为每次使用原型方法时,您的数字都包含在Object.而不是这个:

Number.prototype.mod = function(n) {
  return ((this % n) + n) % n;
}
Run Code Online (Sandbox Code Playgroud)

使用:

function mod(n, m) {
  return ((n % m) + m) % m;
}
Run Code Online (Sandbox Code Playgroud)

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

比原型快〜97%.如果表现当然对你很重要..

  • 微型优化.你必须做一个*大量*的mod计算,以便做出任何改变.编码最清晰,最易维护的代码,然后优化以下性能分析. (7认同)
  • @JeneralJames 更改原型的主要问题是命名空间冲突。归根结底,这只是全球数据的突变。除了小型一次性代码之外,改变全局变量是一种不好的做法。将函数导出为可跟踪依赖项。Polyfills 作为规则的一个例外在这里是无关紧要的。这不是一个 Polyfill。真正的 Polyfill 遵循确保碰撞安全的标准。如果您想原则上争论这一点,那么有一个单独的问题。/sf/ask/435641461/ (6认同)
  • 在这个答案中陈述的动机是微优化,是的,但修改原型是有问题的.喜欢这种方法的副作用最少,就是这个. (3认同)

Rob*_*ers 24

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

-1 % 8 // -1, not 7

  • @DaveKennedy:MDN不是官方语言参考,它是一个社区编辑的网站,有时会出错.[规范](http://www.ecma-international.org/ecma-262/8.0/index.html#sec-applying-the-mod-operator)不称它为模运算符,就我而言可以告诉它从来没有(我回到了ES3).它明确表示运算符产生隐含除法的余数,并将其称为"%运算符". (12认同)
  • “mod”应该从一开始就被实现到每种语言中。经过 30 年的编程生涯,当 a 为负数时,我从不需要 a % b:每一次,我需要的是 mod(a,b)。 (9认同)
  • **应该被称为余数运算符,但它*被称为模运算符:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Arithmetic_operators (7认同)
  • 如果它被称为“余数”,那么根据定义它必须大于 0。你还记得高中时的**除法定理**吗?!所以也许你可以看看这里:https://en.wikipedia.org/wiki/Euclidean_division (2认同)

Sha*_*mal 16

用于返回正结果的"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
Run Code Online (Sandbox Code Playgroud)

而且当然

mod(-13,64) // 51
Run Code Online (Sandbox Code Playgroud)

  • @Shanimal:哈哈!确实如此.HTML编辑器出错.规范文本没有. (3认同)

wis*_*cky 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
Run Code Online (Sandbox Code Playgroud)

  • _"如果Javascript将来改变行为怎么办?"_ - 为什么会这样?不太可能改变这种基本运营商的行为. (19认同)
  • 如果javascript更改模运算符以匹配数学定义,则接受的答案仍然有效. (8认同)
  • 如果`-`,`*`,`/`,`;`,`.`,`(`,`)`,`,`,`Math.floor`,`function`或`return`的行为怎么办变化?然后,您的代码被严重破坏了。 (6认同)
  • +1 分享这种关注和替代特色答案#answer-4467559 &amp; for * 4 * 原因:(1) 为什么它声明,&amp; yes“改变这样一个基本操作的行为是不可能的”但仍然谨慎考虑甚至发现它不需要。(2) 用一个破碎的操作定义一个工作操作,虽然令人印象深刻,但至少在第一次看时是令人担忧的,应该直到没有显示 (3) 虽然我没有很好地验证这个替代方案,但我发现更容易遵循快速浏览。(4)tiny:它使用 1 div+1 mul 而不是 2 (mod) div &amp; 我听说过很多早期的硬件 w/oa 好的 FPU,乘法速度更快。 (2认同)
  • @DestinyArchitect这不谨慎,没有意义.如果他们要改变余数运算符的行为,它将破坏使用它的一系列程序.这永远不会发生. (2认同)

小智 8

JavaScript Modulo操作

成功实施科学计算或算法不仅可以通过了解特定语言或框架提供的功能,还可以了解其局限性.

计算机是精确的科学仪器,但它们通过操纵离散空间中的实体来工作(屏幕上的像素数量有限,每个数字后面的位数有限,等等)

尽量忽略限制或框架规范,很快你就会发现你的数学公式与你尝试编写的代码之间存在阻抗不匹配.

模数运算符

有时,错误地宣传或理解框架功能或操作员会使情况变得复杂.本文重点介绍模运算符.

问任何C#或JavaScript程序员他们的语言中的模运算符是什么,他们很可能会回答:%(例如百分号).大量文档将%符号称为模运算符.

哇!这是一个微妙但非常危险的错误.在C#和JavaScript中,%运算符实际用于计算当一个操作数除以第二个操作数时剩余的余数(带符号).因此,操作数应正确地称为有符号余数运算符.

乍一看,带符号的余数运算符的功能与模运算符类似.让我们通过将JavaScript返回的结果与Google返回的结果进行比较来进行一些测试.

在Chrome中,打开控制台(按F12并选择控制台选项卡).从左列开始逐个输入.接下来在Google搜索栏中输入相同的表达式.注意结果.它们应该是一样的.

                JavaScript  Google
    5 % 3       2           2
    26 % 26     0           0
    15 % 12     3           3
Run Code Online (Sandbox Code Playgroud)

我们现在尝试使用负值作为第一个操作数:

在此输入图像描述

惊喜!

-5%3 = 1(根据谷歌)-5%3 = -2(根据JavaScript)

嗯......如果我们看一下JavaScript中的%运算符的定义(......甚至C#或许多其他语言),这实际上不应该是一个惊喜.谷歌计算真正的模数,而这些计算机语言计算签名提醒.

但是,并非所有编程语言/框架都具有相同的%实现.例如,在Python中,%运算符以与Google相同的方式计算真模数.

在此输入图像描述

语言之间的这种行为差异可能会在计算中引入细微的错误,尤其是当您尝试将算法从一种语言移植到另一种语言时!

理解的问题是问题解决了一半

假设我们需要使用模运算在JavaScript中实现(科学)计算.

由于我们现在明白JavaScript没有真正的模运算符,因此我们可以轻松地将模运算作为函数来实现.

有多种方法可以在JavaScript中实现模数.我将向您展示3种方法.

// 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 );
}
Run Code Online (Sandbox Code Playgroud)

借助我们可以使用的更精确的工具,我们现在已准备好处理该(科学)计算并期望每次都能获得正确的结果.

注意:有很多计算使用模运算...如果你想看看如何在实现Caesar Cipher/ROT13代码中使用这些新的模数函数,你可以查看这篇文章.


Rok*_*jan 8

修复负模(提醒运算符%

使用 ES6 Arrow 函数进行简化,并且不会危险地扩展 Number 原型

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

console.log(mod(-90, 360));    //  270  (Instead of -90)
Run Code Online (Sandbox Code Playgroud)


qua*_*odo 6

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

> -13 & (64 - 1)
51 
Run Code Online (Sandbox Code Playgroud)


dhe*_*aur 5

尽管它的行为不符合您的预期,但这并不意味着 JavaScript 没有“行为”。这是 JavaScript 为求模计算而做出的选择。因为,根据定义,这两个答案都是有意义的。

从维基百科上看这个。您可以在右侧看到不同语言如何选择结果的符号。