警戒位移運算<<的坑

在一次代碼優化的過程當中把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

相關文章
相關標籤/搜索