如何在JavaScript中执行整数除法,并单独获取余数

1353
在JavaScript中,我如何获得以下内容:
1. 一个给定整数被另一个整数整除的次数? 2. 余数?
21个回答

1808

计算除数x对被除数y的商(quotient)[1]和余数(remainder),公式如下:

const quotient = Math.floor(y/x);
const remainder = y % x;

示例:

const quotient = Math.floor(13/3); // => 4 => the times 3 fits into 13  
const remainder = 13 % 3;          // => 1

[1] 一个数字除以另一个数字所得到的整数


108
JavaScript 中 % 运算符可用于浮点数(这与许多其他编程语言不同),可能并不是所期望的:3.5 % 2 的计算结果为1.5。请确保根据需要使用 parseIntfloor 等方法进行处理。 - user166390
22
在数学中,-4.5的整数部分为-5,因为-5是“小于-4.5但仍为最大可能整数”的数。 - Toughy
30
无论你决定如何处理负数,都应该在商和余数之间保持一致。使用floor%结合在这方面不是一致的。要么使用trunc代替floor(从而允许出现负余数),要么使用减法来获取余数(rem = y - div * x)。 - Mark Reed
13
  1. 如果你无论如何都要计算余数rem,那么你可以更快地得到商div,而不必向下取整:(y - rem) / x
  2. 顺便提一句,按照Donald Knuth推荐的定义(符号匹配除数,而不是余数即欧几里德模,也不是JavaScript的符号匹配被除数),我们可以在JavaScript中编写为function mod (a, n) { return a % n + (Math.sign(a) !== Math.sign(n) ? n : 0); }
- Aaron Mansheim
2
-9 / 2 = -4.5。然后你取 -4.5 的底数,即 -5。请记住,-5 小于 -4.5,而底数操作被定义为给定值小于最大整数。 - Mark Reed
显示剩余7条评论

496

我不是位运算符的专家,但这是另一种获取整数的方法:

var num = ~~(a / b);

这个方法同样适用于负数,而 Math.floor() 会向错误的方向舍入。

这个看起来也是正确的:

var num = (a / b) >> 0;

注意:只有在您确信输入范围在32位整数范围内时,才使用{{link1:~~}}作为{{link2:Math.trunc()}}的替代。

106
另一个,我刚刚花了20分钟试图弄清楚它的目的,显然是 a/b | 0 - BlueRaja - Danny Pflughoeft
26
位运算仅适用于整数类型,在JS中也是如此。 ~~ intint | 0int >> 0不会修改初始参数,而是将整数部分传递给运算符。 - Aleksei Zabrodskii
19
“floor”这个词根据它的名字几乎不会将数值朝错误的方向舍入——只是通常人们想要的方向恰恰相反! - Mark K Cowan
36
那是一个buu buu。a = 12447132275286670000; b = 128Math.floor(a/b)的结果是97243220900677100,而~~(a/b)的结果则为-1231452688 - Mirek Rusin
10
注意优先级。~~(5/2)-->2(5/2)>>0-->2,但是~~(5/2)+1-->3,而~~(5/2)>>0+1-->1~~是一个不错的选择,因为它的优先级更加适当。 - timkay
显示剩余15条评论

302

我在Firefox上进行了一些速度测试。

-100/3             // -33.33..., 0.3663 millisec
Math.floor(-100/3) // -34,       0.5016 millisec
~~(-100/3)         // -33,       0.3619 millisec
(-100/3>>0)        // -33,       0.3632 millisec
(-100/3|0)         // -33,       0.3856 millisec
(-100-(-100%3))/3  // -33,       0.3591 millisec

/* a=-100, b=3 */
a/b                // -33.33..., 0.4863 millisec
Math.floor(a/b)    // -34,       0.6019 millisec
~~(a/b)            // -33,       0.5148 millisec
(a/b>>0)           // -33,       0.5048 millisec
(a/b|0)            // -33,       0.5078 millisec
(a-(a%b))/b        // -33,       0.6649 millisec

以上内容基于每个实验10百万次。

结论: 使用 (a/b>>0) (或者 (~~(a/b)) 或者 (a/b|0)) 可以使效率提高约20%。还要记住,当 a/b<0 && a%b!=0 时,它们都与 Math.floor 不一致。


94
请注意,仅在您需要经常进行整数除法计算时,优化其速度才有意义。在其他情况下,我建议选择最简单的方法(无论哪种看起来对您和同事来说都最简单)。 - mik01aj
11
@m01 完全同意 - 在网上过于关注这种东西。 - JonnyRaa
7
哪个更难:学习 Math.floor 和其他许多 API 函数,还是学习 JS 中的位取反运算符 ~,以及位运算如何工作,然后理解双位取反的效果? - Stijn de Witt
28
如果你的同事不用汇编语言编程芯片,他们很可能更容易理解 Math.floor 函数。即使不是这样,这个函数也可以通过谷歌搜索得到答案。 - mik01aj
3
这是整数除法方法的性能比较,不是问题的答案。 - mikemaccana
显示剩余11条评论

270

ES6引入了新的Math.trunc方法。这允许修复@MarkElliot答案,使其也适用于负数:

var div = Math.trunc(y/x);
var rem = y % x;

请注意,Math方法优于位运算符的优点在于它们适用于超过231的数字。


6
@4esn0k 这不是一个bug。你的数字有太多位数,64位二进制格式的IEEE 754数字无法具有如此高的精度。例如,18014398509481984 == 18014398509481985 - Oriol
18014398509481984 == 2**54,我特意使用这个数字,因为它在binary64格式中可以精确表示,答案也是精确的。 - 4esn0k
1
我认为选择很简单:你需要支持最高32位有符号数字吗?使用~~(x/y)。需要支持更大的数字,最高可达54位有符号数字?如果你有Math.trunc,则使用它,否则使用Math.floor(对负数正确)。需要支持更大的数字吗?使用一些大数字库。 - Stijn de Witt
5
针对从谷歌搜索 divmod 的 Ruby 程序员,你可以这样实现它: var div = Math.trunc(x/y); var rem = x % y; return [div, rem]; }该函数返回两个数的数组,第一个数是除法结果,第二个数是余数。 - Alex Moore-Niemi
@AlexMoore-Niemi 这需要执行两次相同的操作。我很确定处理器会将商和模数放在不同的寄存器中进行一次操作,因此JS应该真正提供一种同时获取两者的机制。 - Jez
显示剩余3条评论

56

我通常使用:

const quotient =  (a - a % b) / b;
const remainder = a % b;

可能不是最优雅的方式,但它有效。


3
好的解决方案,因为它避免了解析或截断浮点数带来的丑陋。 - Dem Pilafian
17
如果你需要商和余数,先计算余数,然后在商的表达式中重复使用该值,即商 = (a - 余数) / b; - gb96
5
余数= a % b; 商= (a - 余数) / b; - Zv_oDD

43
var remainder = x % y;
return (x - remainder) / y;

2
这个版本很不幸在 x = -100 时测试失败,因为它返回的是 -34 而不是 -33。 - Samuel
1
那么“var x = 0.3; var y = 0.01;”怎么样?(感谢https://github.com/JuliaLang/julia/issues/4156#issuecomment-23324163) - 4esn0k
1
实际上,@Samuel,对于负值,这种方法返回正确的结果,或者至少返回与使用Math.trunc方法相同的值:)。我已经检查了100,3; -100,3; 100,-3和-100,-3。当然,自从您的评论以来已经过了很长时间,事情也发生了变化。 - Marjan Venema

29

你可以使用parseInt函数来获得截断的结果。

parseInt(a/b)

要获取余数,请使用模运算符:

a%b

parseInt在处理字符串时有一些限制,为了避免这些问题,请使用基数参数并设置为10

parseInt("09", 10)
在某些情况下,数字的字符串表示可能是科学计数法,此时使用parseInt将产生错误的结果。
parseInt(100000000000000000000000000000000, 10) // 1e+32

这个调用将会产生1作为结果。


13
尽可能避免使用parseInt。Douglas Crockford发出了这样的警告:“如果字符串的第一个字符是0,则将在八进制而不是十进制中评估该字符串。在八进制中,8和9不是数字,因此parseInt(“08”)和parseInt(“09”)的结果为0。此错误会导致解析日期和时间的程序出现问题。幸运的是,parseInt可以接受一个基数参数,这样parseInt(“08”,10)就会产生8。我建议您始终提供基数参数。” http://archive.oreilly.com/pub/a/javascript/excerpts/javascript-good-parts/awful-parts.html - Powers
5
在一个除法运算中,我期望得到一个数字而不是一个字符串,但这也是一个好的观点。 - Édipo Costa Rebouças
4
@Powers提到要加上基数(radix),他并没有说要避免使用parseInt,只是要注意其中的一些陷阱。你必须意识到这些问题并准备应对。 - None
4
永远不要使用数字作为参数来调用parseInt函数。parseInt函数用于解析部分包含数字的字符串,而不是截断数字。 - Oriol
4
尽管事物最初没有被设计用于某种特定方式,但这并不意味着你不应该使用它。这个答案可行。 - fregante

9

Math.floor(operation) 返回操作的向下舍入值。

第一个问题的示例:

const x = 5;
const y = 10.4;
const z = Math.floor(x + y);

console.log(z);

第二个问题的示例:

const x = 14;
const y = 5;
const z = Math.floor(x % y);

console.log(x);


5
JavaScript能正确计算负数的floor值以及非整数的余数,这符合数学定义。Floor被定义为“小于参数的最大整数”,因此:
正数:FLOOR(X)=X的整数部分; 负数:FLOOR(X)=X的整数部分减1(因为它必须小于参数,即更加负面!)
余数被定义为除法的“剩余部分”(欧几里得算术)。当被除数不是整数时,商通常也不是整数,即没有余数,但如果将商强制成整数(这就是当某人尝试获取浮点数的余数或模数时发生的情况),显然会有一个非整数的“剩余部分”。
JavaScript确实按预期计算一切,所以程序员必须小心地询问适当的问题(人们也应该小心回答被问到的内容!)。Yarin的第一个问题不是“X除以Y的整数除法是什么”,而是“给定的整数全部去另一个整数的次数”。对于正数,两个答案相同,但对于负数则不同,因为整数除法(被除数除以除数)将比数字(除数)“进入”另一个(被除数)的次数少1。换句话说,Floor将为负数的整数除法返回正确答案,但是Yarin没有问到!
gammax正确回答了该代码按Yarin所要求的方式运行。另一方面,Samuel错了,他没有做数学计算,我想,否则他会看到它确实有效(此外,他没有说他的示例除数是多少,但我希望它是3):
Remainder = X % Y = -100 % 3 = -1
GoesInto = (X - Remainder) / Y = (-100 - -1) / 3 = -99 / 3 = -33
顺便说一下,我在Firefox 27.0.1上测试了该代码,它按预期工作,对于被除数和除数的正负值以及非整数值均可使用,例如:
-100.34 / 3.57:GoesInto = -28,Remainder = -0.3800000000000079
是的,我注意到那里存在精度问题,但我没有时间检查它(我不知道它是Firefox、Windows 7还是我的CPU FPU的问题)。然而,对于只涉及整数的Yarin问题,gammax的代码完全有效。

5

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接