在 JavaScript 中,我如何获得:
给定整数进入另一个整数的次数?其余的?
对于某个数 y
和某个除数 x
,将商 (quotient
) 和余数 (remainder
) 计算为:
var quotient = Math.floor(y/x);
var remainder = y % x;
我不是按位运算符的专家,但这是获得整数的另一种方法:
var num = ~~(a / b);
这也适用于负数,而 Math.floor()
将朝错误的方向舍入。
这似乎也是正确的:
var num = (a / b) >> 0;
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
。 ~~
是一个不错的选择,因为优先级更合适。
我在 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 万次试验。
结论:使用(a/b>>0)
(或(~~(a/b))
或(a/b|0)
)可实现约 20% 的效率增益。另请记住,当 a/b<0 && a%b!=0
时,它们都与 Math.floor
不一致。
Math.floor
和谁知道有多少其他 API 函数,或者了解 ~
(按位非)运算符以及按位运算如何在 JS 中工作,然后是 了解双波浪线的效果?
Math.floor
。即使没有,这个也可以用谷歌搜索。
ES6 引入了新的 Math.trunc
方法。这允许修复 @MarkElliot's answer 以使其也适用于负数:
var div = Math.trunc(y/x);
var rem = y % x;
请注意,与位运算符相比,Math
方法的优势在于它们可以处理超过 231 的数字。
18014398509481984 == 18014398509481985
。
~~(x/y)
。需要支持最大 54 位签名的更大数字?如果有则使用 Math.trunc
,否则使用 Math.floor
(对负数更正)。需要支持更大的数字吗?使用一些大数字库。
divmod
的 rubyists,您可以这样实现它:function divmod(x, y) { var div = Math.trunc(x/y); var rem = x % y; return [div, rem]; }
我通常使用:
const quotient = (a - a % b) / b;
const remainder = a % b;
它可能不是最优雅的,但它确实有效。
var remainder = x % y;
return (x - remainder) / y;
Math.trunc
的方法相同的值:)。我检查了 100,3; -100,3; 100,-3 和 -100,-3。当然,自从您的评论和事情发生变化以来,已经过去了很多时间。
您可以使用函数 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。我建议您始终提供基数参数。” archive.oreilly.com/pub/a/javascript/excerpts/…
parseInt
应该避免;只是有一些问题需要注意。 您必须意识到这些事情并做好应对的准备。
parseInt
。 parseInt
应该解析部分数字字符串,而不是截断数字。
Math.floor(operation)
返回运算的向下舍入值。
第一个问题的例子:
常数 x = 5;常数 y = 10.4;常量 z = Math.floor(x + y);控制台.log(z);
第二个问题的例子:
常数 x = 14;常数 y = 5;常量 z = Math.floor(x % y);控制台.log(x);
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:进入 = -28,余数 = -0.3800000000000079
是的,我注意到了,那里有一个精度问题,但我没有时间检查它(我不知道这是 Firefox、Windows 7 还是我的 CPU 的 FPU 的问题)。不过,对于 Yarin 的问题,它只涉及整数,gammax 的代码可以完美运行。
Alex Moore-Niemi 的评论作为答案:
对于来自 Google 搜索 divmod
的 Rubyists,您可以这样实现它:
function divmod(x, y) {
var div = Math.trunc(x/y);
var rem = x % y;
return [div, rem];
}
结果:
// [2, 33]
divmod
使用下限除法 (Math.floor
),这与涉及负数时的截断除法 (Math.trunc
) 不同。 NPM divmod
package、Ruby divmod
、SWI-Prolog divmod
和可能的许多其他实现也是如此。
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)]; }
。
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 Quotient of two value
console.log(divide.division) //to get Floating division of two value
console.log(divide.Remainder) //to get Remainder of two value
console.log(divide.calculate()) //to get object containing all the values
您也可以使用三元来决定如何处理正整数和负整数值。
var myInt = (y > 0) ? Math.floor(y/x) : Math.floor(y/x) + 1
如果这个数字是正数,一切都很好。如果数字是负数,它会加 1,因为 Math.floor 处理负数的方式。
这将始终截断为零。不知道是否为时已晚,但它是这样的:
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
};
}
计算页数可以一步完成:Math.ceil(x/y)
如果您需要计算 JS 运行时无法表示的非常大整数的余数(任何大于 2^32 的整数都表示为浮点数,因此它会丢失精度),您需要做一些技巧。
这对于检查我们日常生活中许多情况中存在的许多校验位(银行帐号、信用卡等)尤为重要
首先,您需要将您的数字作为字符串(否则您已经失去了精度并且其余的没有意义)。
str = '123456789123456789123456789'
您现在需要将字符串分成更小的部分,足够小,以便任何余数和一段字符串的串联可以容纳 9 位数字。
digits = 9 - String(divisor).length
准备一个正则表达式来拆分字符串
splitter = new RegExp(`.{1,${digits}}(?=(.{${digits}})+$)`, 'g')
例如,如果 digits
为 7,则正则表达式为
/.{1,7}(?=(.{7})+$)/g
它匹配最大长度为 7 的非空子字符串,后跟((?=...)
是正向预测)多个 7 的字符。“g”是让表达式遍历所有字符串,而不是在第一场比赛。
现在将每个部分转换为整数,并通过 reduce
计算余数(加回之前的余数 - 或 0 - 乘以 10 的正确幂):
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]); } 让 x = intDivide(4,5);让 y = intDivide(5,5);让 z = intDivide(6,5);控制台.log(x);控制台.log(y);控制台.log(z);
3.5 % 2
的计算结果为 1.5。确保按要求处理(parseInt、floor 等)floor
和%
并不一致。使用trunc
而不是floor
(从而允许负余数)或使用减法来获得余数(rem = y - div * x
)。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); }
的内容。