警戒位移运算<<的坑

在一次代码优化的过程当中把html

// a,b都为正整数且大于0
while (a>=b) {
    a-=b;
}
复制代码

优化为bash

// a,b都为正整数且大于0
while (a>=b) {
    let tmpB = b;
    while (a>=tmpB) {
        let tmpShiftB = tmpB<<1;
        if (a>=tmpShiftB) {
            tmpB = tmpShiftB;
        } else {
            a-=tmpB;
        }
    }
}
复制代码

本意是若是a很大,b很小的状况,也能把快速进行减法运算。
但后来发现a一旦很大,就会死循环,这个仍是几率出现。
后来debug发现到必定时候tmpShiftB会变成0,致使死循环。
当时就想确定是位移符的坑,后来发现有下面问题
优化

tmpShiftB到达2147483648后左移一位变0
复制代码

我一看乐了,这不是2的-32次方嘛,确定是JS当有符号的int32型算了,而后溢出了,八成是谷歌引擎的的BUG!
但想一想别高兴的太早,看看标准怎么说,毕竟制定标准的人也贼坑。让咱们看看标准怎么写的。
ui

ecma-262的第9版左位移说明lua

12.9.3The Left Shift Operator ( << )
NOTE
Performs a bitwise left shift operation on the left operand by the amount specified by the right operand.

12.9.3.1Runtime Semantics: Evaluation
ShiftExpression:ShiftExpression<<AdditiveExpression
Let lref be the result of evaluating ShiftExpression.
Let lval be ? GetValue(lref).
Let rref be the result of evaluating AdditiveExpression.
Let rval be ? GetValue(rref).
Let lnum be ? ToInt32(lval).
Let rnum be ? ToUint32(rval).
Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.
Return the result of left shifting lnum by shiftCount bits. The result is a signed 32-bit integer.
复制代码

翻译下来大概意思是:spa

左表达式<<右表达式

左表达式结果转成Int32(有符号的int的32位类型),结果取名lnum

右表达式结果转成Uint32(无符号的int的32未类型),同时进行& 0x1F运算(即保留2进制的后5位,再白话一点就是保留32之内的位的数值,但和%32又有些不一样),结果取名shiftCount

最后再把lnum左位移shiftCount位,并把这个结果再转换成有符号的int的32位类型

复制代码

一看下来坏了,果真是标准坑爹,且不说左表达式结果转成了有符号的int的32位类型,位移后的结果也给转成了有符号的int的32位类型。果真是标准坑爹。翻译

结论

看来之后使用左位移符都要当心了,只适用于int32的范围(-2^32~2^32),要是有可能超过,看来是断断不能用了。看来JS的世界精确整数也不必定就是(-2^53~2^53)范围了。debug

相关文章
相关标签/搜索