先日のPHP版のついでに。
似たような事情ではありますが、こちらはわりと歴史的な現象のようで。
しかし今まで気にしたこともなかった驚愕の事実。
大きい値が浮動小数扱いな件
underflow.js
console.log((0xfedcba98765438ff).toString(16));
console.log((0xfedcba9876543cff).toString(16));
fedcba9876543800
fedcba9876544000
符号+仮数部の53bitで下側が消滅します。
さらに、四捨五入というか零捨一入で上側まで破壊される問題もあり。
そもそもintがsigned 32bitな件
msb.js
console.log(1<<31);
-2147483648
しかし16進数表記はより大きい値でも受け入れるので、ややこしい事態に…
なんか壊れてる件
broken.js
console.log((0x40000000<<1).toString(16)); // 80000000 になってほしいのに
console.log((0x80000000>>1).toString(16)); // c0000000 になってほしいのに
console.log((0x80000000|1).toString(16)); // 80000001 になってほしいのに
-80000000
-40000000
-7fffffff
論理演算自体は正常
ok.js
console.log(0x40000000<<1); // 80000000 → -2147483648
console.log(0x80000000>>1); // c0000000 → -1073741824
console.log(0x80000000|1); // 80000001 → -2147483647
-2147483648
-1073741824
-2147483647
toString(16) が符号外して処理されるようで。
16進表記が符号なしで解釈されるなら、必然的な仕様といえるでしょう。
というわけで…
32bit前提なら、int→16進文字列変換さえ正常化すれば何とか。
dexhex32.js
var dechex32=function(src){
// 16bitずつ文字列化作戦
var h=(src>>16)&0xffff;
var l=src&0xffff;
// 上側が0なときは下側だけ表示
if(!h)return l.toString(16);
// 上側があるとき下側は必ず4桁
return h.toString(16)+('000'+l.toString(16)).slice(-4);
};
console.log(dechex32(0x40000000<<1));
console.log(dechex32(0x80000000>>1));
console.log(dechex32(0x80000000|1));
80000000
c0000000
80000001