JavaScript % (modulo) 给出负数的负数结果
Posted
技术标签:
【中文标题】JavaScript % (modulo) 给出负数的负数结果【英文标题】:JavaScript % (modulo) gives a negative result for negative numbers 【发布时间】:2011-05-26 22:27:37 【问题描述】:根据Google Calculator(-13) % 64
是51
。
根据 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 中,您的n
s 和 m
s 使用错误。应该是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 & (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(数论+树状数组)