前言
原因是在刷 leetcode 的时候因为位运算导致的期待值错误死活无法通过,也增加了一些js中关于位运算的知识。
首先先回顾一下自己所遇到的问题,在使用迭代法解答剑指 Offer 16. 数值的整数次方问题时,发现对于一个测试用例超时:1.00000 -2147483648
本来的解答是:
function myPow(x: number, n: number): number {
let ans = 1;
let exponent = Math.abs(n);
for (let i = exponent; i != 0; i >>= 1, x *= x) {
if (i % 2) {
ans *= x
}
}
return n < 0 ? 1 / ans : ans;
};
于是在循环中加入了一条 console.log(i)
发现 i
变成了负数,最后一直是 -1,导致死循环。
WTF???咋回事。
于是控制台手动计算:
2147483648 >> 1
怎么会这样,******!
但是 num / 2
没问题啊,就先看看2进制吧:
num.toString(2)
// "10000000000000000000000000000000"
num.toString(2).length
// 32
要变成负数,这是无符号右移,说明符号位是1,可是这符号位不是1啊,毕竟 js 中是以 64 位双精度存储。
老老实实先用除法解决问题吧。
后来经过一番搜索,发现自己还是大意了,没看过标准还是得吃亏,es 标准说 js 中的位运算针对的都是 32 位整型,也就是说 js 在进行位运算操作时,先会将数字转为 32 位的整型,然后再进行位运算操作。这也就很清楚的解释了刚才所遇到的问题。
The production A : A @ B, where @ is one of the bitwise operators in the productions above, is evaluated as follows:
- Let lref be the result of evaluating A.
- Let lval be GetValue(lref).
- Let rref be the result of evaluating B.
- Let rval be GetValue(rref).
- Let lnum be ToInt32(lval).
- Let rnum be ToInt32(rval).
- Return the result of applying the bitwise operator @ to lnum and rnum. The result is a signed 32 bit integer.
注: 第5和第6步清除的说明了情况。
先前只知道 js 位运算需要先转换为整数,但是没想到是 32 位,一直天真的认为是 64 位,这下长记性了。
结论
没有什么必要在 js 中使用位运算,未必就能提高性能,引擎未必不会帮我们优化,由于 number 类型存储的方式,就可能会踩坑。
参考资料