提问人:Yarin 提问时间:11/20/2010 最后编辑:Peter MortensenYarin 更新时间:10/14/2023 访问量:1184471
如何在 JavaScript 中执行整数除法,并单独获取余数
How to perform an integer division, and separately get the remainder, in JavaScript
答:
对于某个数和某个除数,计算商 ()[1] 和余数 () 如下:y
x
quotient
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] 一个数字除以另一个数字得到的整数
评论
3.5 % 2
floor
%
trunc
floor
rem = y - div * x
rem
div
(y - rem) / x
function mod (a, n) { return a % n + (Math.sign(a) !== Math.sign(n) ? n : 0); }
我不是按位运算符方面的专家,但这是获取整数的另一种方法:
var num = ~~(a / b);
这也适用于负数,但会向错误的方向四舍五入。Math.floor()
这似乎也是正确的:
var num = (a / b) >> 0;
注意:仅当您确信输入范围在 32 位整数范围内时,才使用 ~~
作为 Math.trunc()
的替代品。
评论
a/b | 0
~~int
int | 0
int >> 0
floor
鉴于它的名字,几乎不会朝着错误的方向四舍五入 - 只是不是人们通常想要的方向!
a = 12447132275286670000; b = 128
Math.floor(a/b)
97243220900677100
~~(a/b)
-1231452688
~~(5/2) --> 2
(5/2)>>0 --> 2
~~(5/2) + 1 --> 3
~~(5/2)>>0 + 1 --> 1
~~
var remainder = x % y;
return (x - remainder) / y;
评论
Math.trunc
我在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
以上是基于每个试验的 1000 万次试验。
结论:使用(或或)可实现约20%的效率增益。还要记住,它们都与 、 不一致。(a/b>>0)
(~~(a/b))
(a/b|0)
Math.floor
a/b<0 && a%b!=0
评论
Math.floor
~
Math.floor
JavaScript 按照负数的数学定义计算负数和非整数的余数的下限。
FLOOR 被定义为“小于参数的最大整数”,因此:
- 正数:FLOOR(X)=X 的整数部分;
- 负数:FLOOR(X)=X 减去 1 的整数部分(因为它必须小于参数,即更负!
REMAINDER 被定义为除法的“剩余”(欧几里得算术)。当被除数不是整数时,商通常也不是整数,即没有余数,但如果商被强制为整数(当有人试图获得浮点数的余数或模数时,就会发生这种情况),显然会有一个非整数“剩余”。
JavaScript 确实按预期计算了一切,因此程序员必须小心地提出正确的问题(人们应该小心地回答所问的问题!Yarin的第一个问题不是“X除以Y的整数除以多少”,而是“一个给定整数进入另一个整数的整数”。对于正数,两者的答案是相同的,但对于负数则不然,因为整数除法(除数除数)将比一个数(除数)“进入”另一个数(除数)的次数小 -1。换句话说,FLOOR 将返回负数整数除法的正确答案,但 Yarin 没有问这个问题!
gammax 回答正确,该代码按照 Yarin 的要求工作。另一方面,塞缪尔错了,我没有做数学,我猜,否则他会看到它确实有效(另外,他没有说他的例子的除数是多少,但我希望它是 3):
余数 = X % Y = -100 % 3 = -1
GoesInto = (x - 余数) / y = (-100 - -1) / 3 = -99 / 3 = -33
顺便说一句,我在 Firefox 27.0.1 上测试了代码,它按预期工作,具有正数和负数以及非整数值,包括除数和除数。例:
-100.34 / 3.57:GoesInto = -28,余数 = -0.3800000000000079
是的,我注意到,那里有精度问题,但我没有时间检查它(我不知道这是 Firefox、Windows 7 还是我的 CPU 的 FPU 有问题)。然而,对于Yarin的问题,它只涉及整数,gammax的代码可以完美地工作。
ES6 引入了新的 Math.trunc
方法。这允许修复@MarkElliot的答案,使其也适用于负数:
var div = Math.trunc(y/x);
var rem = y % x;
请注意,与按位运算符相比,方法的优点是它们处理超过 231 的数字。Math
评论
18014398509481984 == 18014398509481985
~~(x/y)
Math.trunc
Math.floor
divmod
function divmod(x, y) { var div = Math.trunc(x/y); var rem = x % y; return [div, rem]; }
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);
您可以使用该函数获取截断的结果。parseInt
parseInt(a/b)
要获取余数,请使用 mod 运算符:
a%b
parseInt 在字符串方面存在一些陷阱,以避免使用基数为 10 的基数参数
parseInt("09", 10)
在某些情况下,数字的字符串表示可以是科学记数法,在这种情况下,parseInt 会产生错误的结果。
parseInt(100000000000000000000000000000000, 10) // 1e+32
此调用将生成 1 作为结果。
评论
parseInt
应尽可能避免使用。这是道格拉斯·克罗克福德(Douglas Crockford)的警告:“如果字符串的第一个字符是0,则该字符串以8为基数而不是以10为基数计算。以 8 为基数,8 和 9 不是数字,因此 parseInt(“08”) 和 parseInt(“09”) 生成 0 作为结果。此错误会导致分析日期和时间的程序出现问题。幸运的是,parseInt 可以接受基数参数,因此 parseInt(“08”, 10) 生成 8。我建议您始终提供 radix 参数。archive.oreilly.com/pub/a/javascript/excerpts/......
parseInt
parseInt
parseInt
您也可以使用三元运算符来决定如何处理正整数和负整数值。
var myInt = (y > 0) ? Math.floor(y/x) : Math.floor(y/x) + 1
如果数字是正数,则一切都很好。如果数字为负数,则由于 Math.floor 处理负数的方式,它将加 1。
这将始终被截断为零。
function intdiv(dividend, divisor) {
divisor = divisor - divisor % 1;
if (divisor == 0) throw new Error("division by zero");
dividend = dividend - dividend % 1;
var rem = dividend % divisor;
return {
remainder: rem,
quotient: (dividend - rem) / divisor
};
}
我通常使用:
const quotient = (a - a % b) / b;
const remainder = a % b;
它可能不是最优雅的,但它有效。
评论
Alex Moore-Niemi 的评论作为回答:
对于来自 Google 的 Ruby 主义者来说,你可以这样实现它:divmod
function divmod(x, y) {
var div = Math.trunc(x/y);
var rem = x % y;
return [div, rem];
}
结果:
// [2, 33]
评论
divmod
以及许多其他实现也是如此。
divmod
Math.floor
Math.trunc
divmod
如果只是用 2 的幂除法,则可以使用按位运算符:
export function divideBy2(num) {
return [num >> 1, num & 1];
}
export function divideBy4(num) {
return [num >> 2, num & 3];
}
export function divideBy8(num) {
return [num >> 3, num & 7];
}
(第一个是商,第二个是余数)
评论
function divideByPowerOf2(num, exponent) { return [num >> exponent, num & ((1 << exponent) - 1)]; }
计算页数可以通过一个步骤完成:
Math.ceil(x/y)
评论
如果你需要计算非常大的整数的余数,而 JS 运行时无法这样表示(任何大于 2^32 的整数都表示为浮点数,因此它失去了精度),你需要做一些技巧。
这对于检查我们日常生活中许多情况下存在的许多校验位(银行帐号、信用卡等)尤为重要。
首先,你需要你的数字作为一个字符串(否则你已经失去了精度,其余的没有意义)。
str = '123456789123456789123456789'
您现在需要将字符串拆分为较小的部分,足够小,以便任何余数和字符串的连接可以容纳 9 位数字。
digits = 9 - String(divisor).length
准备一个正则表达式来拆分字符串
splitter = new RegExp(`.{1,${digits}}(?=(.{${digits}})+$)`, 'g')
例如,如果为 7,则正则表达式为digits
/.{1,7}(?=(.{7})+$)/g
它匹配最大长度为 7 的非空子字符串,该子字符串后面是 7 的倍数字符(是正面展望)。“g”是使表达式遍历所有字符串,而不是在第一次匹配时停止。(?=...)
现在将每个部分转换为整数,并通过以下方式计算余数(将前面的余数 - 或 0 - 乘以 10 的正确幂相加):reduce
reducer = (rem, piece) => (rem * Math.pow(10, digits) + piece) % divisor
由于“减法”余数算法,这将起作用:
n mod d = (n - kd) mod d
它允许用其余数替换数字十进制表示的任何“初始部分”,而不会影响最终余数。
最终代码如下所示:
function remainder(num, div) {
const digits = 9 - String(div).length;
const splitter = new RegExp(`.{1,${digits}}(?=(.{${digits}})+$)`, 'g');
const mult = Math.pow(10, digits);
const reducer = (rem, piece) => (rem * mult + piece) % div;
return str.match(splitter).map(Number).reduce(reducer, 0);
}
这是执行此操作的一种方法。(就我个人而言,我不会这样做,但我认为这是一种有趣的方式。前面的答案中提到的方法肯定更好,因为这会调用多个函数,因此速度较慢,并且在捆绑包中占用更多空间。
function intDivide(numerator, denominator) {
return parseInt((numerator/denominator).toString().split(".")[0]);
}
let x = intDivide(4,5);
let y = intDivide(5,5);
let z = intDivide(6,5);
console.log(x);
console.log(y);
console.log(z);
评论
用:
const idivmod = (a, b) => [a/b |0, a%b];
还有一个关于它的建议:模数和附加整数数学
function integerDivison(dividend, divisor) {
this.Division = dividend/divisor;
this.Quotient = Math.floor(dividend/divisor);
this.Remainder = dividend%divisor;
this.calculate = () => {
return {Value:this.Division, Quotient:this.Quotient, Remainder:this.Remainder};
}
}
var divide = new integerDivison(5, 2);
console.log(divide.Quotient) // To get the quotient of two values
console.log(divide.division) // To get the floating division of two values
console.log(divide.Remainder) // To get the remainder of two values
console.log(divide.calculate()) // To get the object containing all the values
如果你需要一些愚蠢的大数字的商,你可以使用:
Math.trunc((x/y) + (Number.EPSILON * (2 ** Math.ceil(Math.log2(Math.abs(x/y))))) * Math.sign(x/y))
注意:这仅适用于 和 值(即被除数和除数)被准确表示的情况,即使在任何四舍五入以使它们大于 时作为整数工作的情况也是如此。x
y
Number.MAX_SAFE_INTEGER
例如,如果我们有:
x = 45000000000000000000000000000 = 4.5e+28
y = 500000000000000000000000000 = 5e+26
然后,此页面上给出的答案会为您提供:
89.99999999999999: x/y
90: Math.trunc((x/y) + (Number.EPSILON * (2 ** Math.ceil(Math.log2(Math.abs(x/y))))) * Math.sign(x/y))
89: Math.floor(x/y)
89: ~~(x/y)
89: (x/y)>>0
89: x/y|0
89: (x-(x%y))/y
正确答案是 ,所以,正如你所看到的,我上面给出的方程式是唯一提供正确答案的方程式。90
该等式也适用于负面结果。如果我们定为负数,那么我们得到:x
-89.99999999999999: x/y
-90: Math.trunc((x/y) + (Number.EPSILON * (2 ** Math.ceil(Math.log2(Math.abs(x/y))))) * Math.sign(x/y))
-90: Math.floor(x/y)
-89: ~~(x/y)
-89: (x/y)>>0
-89: x/y|0
-89: (x-(x%y))/y
只有那个等式并给出正确的答案。Math.floor()
而且,只是为了确认一些不同的值,这些值会给出稍大的值:
x = -45000000000000000000000000 = -4.5e+25
y = 500000000000000000000000 = 5e+23
我们得到:
-90.00000000000001: x/y
-90: Math.trunc((x/y) + (Number.EPSILON * (2 ** Math.ceil(Math.log2(Math.abs(x/y))))) * Math.sign(x/y))
-91: Math.floor(x/y)
-90: ~~(x/y)
-90: (x/y)>>0
-90: x/y|0
-90.00000000000001: (x-(x%y))/y
在这种情况下,并且失败了,这意味着,虽然它可能不快或不漂亮,但为这个答案给出的代码是唯一在所有情况下都能给出正确结果的方法,前提是除数和被除数能够准确表示。(或者,至少,我所知道的所有案例。Math.floor()
(x-(x%y))/y
如果您想知道如何获得大数的正确余数,请参阅:
补遗:如果你只使用正数,那么你可以把它缩短为这样:
Math.trunc((x/y) + (Number.EPSILON * (2 ** Math.ceil(Math.log2(x/y)))))
对于满足这些约束的基元函数(计算除法的商)和(计算除法的余数),有几种可能的定义:div
mod
Number.isInteger(div(x, y))
;x === div(x, y) * y + mod(x, y)
;Math.abs((mod(x, y)) < Math.abs(y)
.
计算机科学文献和编程语言中常用的定义基于
截断的除法:
function div(x, y) { return Math.trunc(x / y); } function mod(x, y) { return x % y; }
地板部门:
function div(x, y) { return Math.floor(x / y); } function mod(x, y) { return ((x % y) + y) % y; }
欧几里得分部:
function div(x, y) { return Math.sign(y) * Math.floor(x / Math.abs(y)); } function mod(x, y) { const z = Math.abs(y); return ((x % z) + z) % z; }
此外
- 截断的除法具有以下属性:
mod(x, y) * x >= 0
; - The Floored Division具有以下属性:
mod(x, y) * y >= 0
; - 欧几里得除法具有以下性质: .
mod(x, y) >= 0
因此
- 如果 和 ,则截断、地板和欧几里得除法一致;
x >= 0
y > 0
- 如果 和 ,则截断除法和欧几里得除法一致;
x >= 0
y < 0
- 如果 和 ,则 floored 和 Euclidee 划分一致;
x <= 0
y > 0
- 如果 和 ,则截断的除法和下限的除法一致。
x <= 0
y < 0
建议选择欧几里得除法而不是截断和地板除法来定义函数,并且根据 Raymond Boute 的论文“函数 div 和 mod 的欧几里得定义”:div
mod
在本文中,我们澄清了各种定义之间的差异,特别是那些基于截断除法(T定义)和Knuth定义的基于地板除法(F定义)的定义。我们还提出了另一个定义,我们称之为欧几里得定义,因为它基于欧几里得定理(E-definition)。这种替代方案在文献中很少讨论,但仔细分析,无论是在理论上还是在实际使用中,它在规律性和有用的数学性质方面都是有利的。欧几里得定义通常是最直接的选择,在各种具有代表性的应用领域中,我们体验到了对div-mod函数对的需求。
我们可以使用以下方法。
quotient = dividend / divisor | 0;
以及我们可以通过模运算符获得提醒的任何方式
remainder = dividend % divisor;
上一个:如何检查 NaN 值
评论