今日、恐ろしいことに気がついた。
386互換 CPU*1上で shl eax, cl を使って eax を 32bit シフトすると、ゼロクリアされないのである。演算の結果は、シフト前と同じ。すなわち、clの下位5bit は無視されるのである。
まあCPUの命令ならそういうこともあるかもしれないが、某社のコンパイラでコンパイルすると、operator<< がそのまま shl になってしまうので、
#include <stdio.h> typedef unsigned int uint32; typedef unsigned __int64 uint64; template< typename T, int bits > void test( char * format ) { T complietime_value = (~T(0))<<bits; T runtime_value = (~T(0))<<((new char)?bits:0); // to supress optimize printf( format, bits, complietime_value, runtime_value ); } int main() { test<uint32,31>("uint32<<%d : %x / %x\n"); test<uint32,32>("uint32<<%d : %x / %x\n"); test<uint64,63>("uint64<<%d : %I64x / %I64x\n"); test<uint64,64>("uint64<<%d : %I64x / %I64x\n"); return 0; }
uint32<<31 : 80000000 / 80000000 uint32<<32 : 0 / ffffffff uint64<<63 : 8000000000000000 / 8000000000000000 uint64<<64 : 0 / 0
のようになる。32bit 値を 32bit シフトする計算で、runtime の計算とコンパイル時の計算で結果が違っている。どうも、コンパイル時は真面目に計算しているらしい。
bcc5.5.1 でも試してみたところ、bcc5.5.1 では
uint32<<31 : 80000000 / 80000000 uint32<<32 : ffffffff / ffffffff uint64<<63 : 8000000000000000 / 8000000000000000 uint64<<64 : 0 / 0
と、一致した。コンパイル時の計算も shl そのまんまのようだ。
また、コンパイル時に警告が出た*3。
shl そのまんまでゼロクリアされないのはどうかと思うが、多少はましか。
C++ の仕様への準拠ということでは、どうなんだろう??
まあ、x86 は珍妙なCPUのようなので、そういうこともあるだろう。なんて思ってしまう。
それと。
document.write( '1<<32 = '+(123<<32)+'<br>' ) document.write( '1<<31 = '+(123<<31)+'<br>' )
とかやったら、Opera, IE6, Mozilla FireFox のどれでも、123<<32 は 123 だった。それでいいのか??
非 x86 環境でどうなるのか興味あるが、持ってないのでわからない。