我一直在用Javascript进行一些测试,我会展示它们(从控制台粘贴):
-1 % 4
-1
-4 % 4
-0
-8 % 4
-0
-6 % 4
-2
-6 % -4
-2
6 % -4
2
-1%4是真正的-1,但是,我真的不明白为什么Javascript不会产生3,这同样正确,但更具说服力。
什么是-0?我知道-4 / 4 = -1,这是一个整数,但我不确定-0是什么。
我对Javascript计算模数感到困惑。
我的测试是出于一个令人不快的惊喜,我一直在画廊工作,我有一个活跃图片的索引。有两个按钮,用于下一个和上一个按钮。最后一个图像的下一个图像是第一个图像,第一个图像的前一个图像是最后一个图像。我一直在改变索引:
currentImageIndex = (currentImageIndex + 1) % images.length;
当用户点击下一个按钮时。当用户点击上一个按钮时,我一直在尝试使用以下代码:
currentImageIndex = (currentImageIndex - 1) % images.length;
我很惊讶地看到后者不能很好地工作,因为之前的图像没有显示,并且因为在currentImageIndex索引的数组中使用了无效索引而引发了错误。我有console.log-ed值,并看到它是-1!好的,我已解决过这个问题:
currentImageIndex = (currentImageIndex + images.length - 1) % images.length
并没有太痛苦,但我仍然不理解计算结果背后的逻辑。所以,是否有人知道Javascript的模数计算是如何工作的,因为我真的很困惑,我认为-0因-4%4而成为本月的笑话。
答案 0 :(得分:4)
很棒的问题!
在我看来,ECMA令人困惑,很容易错过模运算符的工作方式(我当然这样做了)。从ECMA-262 §11.5.3开始,声明:
由%运算符...
计算的浮点余数运算的结果
推断余数运算。它继续提供算法:
......既没有无穷大,也没有零,也没有涉及NaN 从被除数n和除数d得到的浮点余数r 由数学关系r = n - (d×q)定义,其中q是a 仅当n / d为负时才为负的整数,仅当为n时为正 n / d是正的,其大小尽可能大 超过n和d的真实数学商的大小。 计算r并使用舍入到最接近的可表示值 IEEE 754舍入到最接近的模式。
将其应用于-1 % 4
的情况,然后:
n = -1
d = 4
trueMathematicalQuotient = -1/4 = -0.25
因此q
必须为负数,因为n/d
为负数。 d (4)的最大负整数可以乘以小于或等于-0.25的幅度,并且给出的结果小于或等于 n (-1)是-0(注意-1比{-0.25更大magnitude。)
这样做的简单方法是将 q 截断为整数:
q = -0 // -0.25 truncated
将数字放入等式中:
r = -1 - (4 * 0)
r = -1 - 0
r = -1
可以将其放在函数中:
function remainderMod(n, d) {
var q = parseInt(n / d); // truncates to lower magnitude
return n - (d * q);
}
或缩写为:
function remainderMod(n, d) {
return n - (d * (n/d | 0));
}
答案 1 :(得分:2)
OP希望了解发生了什么(不一定是"修复"它)。简短的回答是JavaScript有一个"余数"操作员不是"模数"运算符(according to Douglas Crawford)。完全不同,这里是a description of modulo vs remainder(特别是指C#)。
编辑:最后,the spec有明确的答案。
答案 2 :(得分:2)
实际上在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 );
}
因此,让我们理解为什么要加倍努力并定义上述功能之一,而不是使用%运算符。
成功实施科学计算或算法不仅可以通过了解特定语言或框架提供的功能,还可以了解其局限性。
尝试忽略限制或框架规范,很快您就会发现数学公式与您尝试编写的代码之间存在阻抗不匹配。
有时,错误地宣传或理解框架功能或操作员会使情况变得复杂。本文重点介绍模运算符。
询问任何C#或JavaScript程序员他们的语言中的模运算符是什么,他们很可能会回答:%(例如百分号)。大量文档将%符号称为模运算符。
哇!这是一个微妙但非常危险的错误。在C#和JavaScript中,%运算符实际用于计算当一个操作数除以第二个操作数时剩余的余数(带符号)。因此,操作数应正确地称为有符号余数运算符。
乍一看,带符号的余数运算符的功能与模运算符类似。让我们通过将JavaScript返回的结果与Google返回的结果进行比较来进行一些测试。
在Chrome中,打开控制台(按F12并选择控制台选项卡)。从左列开始逐个输入。接下来在Google搜索栏中输入相同的表达式。注意结果。它们应该是一样的。
JavaScript Google
5 % 3 2 2
26 % 26 0 0
15 % 12 3 3
现在让我们尝试使用负值作为第一个操作数:
惊喜!
-5%3 = 1(根据Google) -5%3 = -2(根据JavaScript)
嗯......如果我们看一下JavaScript中的%运算符(或者甚至是C#或许多其他语言)的定义,这实际上不应该是一个惊喜。 Google会计算真正的模数,而这些计算机语言会计算签名提醒。
但是,并非所有编程语言/框架都具有相同的%实现。例如,在Python中,%运算符以与Google相同的方式计算真模数。
语言之间的这种行为差异可能会在计算中引入细微的错误,尤其是当您尝试将算法从一种语言移植到另一种语言时!
我们可以使用更精确的工具,现在我们已准备好解决(科学)计算,并期望每次都能获得正确的结果。
请注意:有很多的计算方法,利用模运算的...如果你想了解如何在执行的恺撒密码/ ROT13代码中使用这些新的模功能,可以检查这个article < / p>
答案 3 :(得分:0)
查看this question和this answer哪个有解决方案。
您遇到的问题是JavaScript的%
运算符不是真正的模数,而是简单的余数。
如果上述链接断开,这是解决方案:
Number.prototype.mod = function(n) {
return ((this%n)+n)%n;
}
可以这样使用:
(-1).mod(4); // returns 3
如果您不想改变Number
的原型,您只需创建一个功能:
function modulo(a, b) {
return ((a % b) + b) % b
}
可以这样使用:
modulo(-1, 4); // returns 3
答案 4 :(得分:0)
模数函数是余数。所以10%4表示&#34;将10除以4并返回余数&#34;当余数为0时,你知道它是完全可分的。所以-6%4 = -4。发生这种情况是因为Javascript将-6视为不存在的数字,或者另一种类型为0,因此在将其除以整数0次后,您将留下该负数。这是已知的bug。