PHPにおけるビット演算と、bcand(), bcor(), bcxor()関数
2010年10月11日
先のSHA512の計算には、64ビット整数での演算が必要で、これを8バイトの文字列として扱って演算をエミュレートした。他方、SHA1の方は、32ビット整数の演算で計算できる。将来的に、SHA1-HMACを使う可能性があり、この計算にはhash()関数が必要なので、これについてもPHPでエミュレートすることを考えている。
そこで、PHPにおいて32ビット整数のビット演算がちゃんとできるのかどうかを調べることにした。
確認は、下記コードにて。ビット演算すると、32ビット整数として扱ったような符号の反転が起こるようだ。結果が負の数になった場合に4294967296を加えることで、正確に演算できるらしい。4294967296を足した後は64ビット浮動小数点として扱われているように見える。
なお、検証コードでは、bcadd, bcor, bcxorという3つのBC Math関数を作成して、結果の比較を行った。
そこで、PHPにおいて32ビット整数のビット演算がちゃんとできるのかどうかを調べることにした。
確認は、下記コードにて。ビット演算すると、32ビット整数として扱ったような符号の反転が起こるようだ。結果が負の数になった場合に4294967296を加えることで、正確に演算できるらしい。4294967296を足した後は64ビット浮動小数点として扱われているように見える。
なお、検証コードでは、bcadd, bcor, bcxorという3つのBC Math関数を作成して、結果の比較を行った。
<?php
for($i=0;$i<100000;$i++){
$r1=rand(0,0x7fffffff)+rand(0,0x7fffffff)+rand(0,1);
$r2=rand(0,0x7fffffff)+rand(0,0x7fffffff)+rand(0,1);
test($r1,$r2);
}
function test($r1,$r2){
echo "$r1 $r2\n";
$a1=$r1 & $r2;
if ($a1<0) $a1+=4294967296;
$a1=(string)$a1;
$a2=bcand($r1,$r2);
if ($a1!=$a2) echo('error!');
$a1=$r1 | $r2;
if ($a1<0) $a1+=4294967296;
$a1=(string)$a1;
$a2=bcor($r1,$r2);
if ($a1!=$a2) echo('error!');
$a1=$r1 ^ $r2;
if ($a1<0) $a1+=4294967296;
$a1=(string)$a1;
$a2=bcxor($r1,$r2);
if ($a1!=$a2) echo('error!');
}
function bcand($a,$b){
return _bc($a,$b,'&');
}
function bcor($a,$b){
return _bc($a,$b,'|');
}
function bcxor($a,$b){
return _bc($a,$b,'^');
}
function _bc($a,$b,$mode){
$a=(string)$a;
$b=(string)$b;
$res='0';
$op='1';
while($a!='0' || $b!='0'){
$aa=(int)bcmod($a,'65536');
$a=bcsub($a,$aa);
$a=bcdiv($a,'65536');
$bb=(int)bcmod($b,'65536');
$b=bcsub($b,$bb);
$b=bcdiv($b,'65536');
switch($mode){
case '&':
$temp=$aa & $bb;
break;
case '|':
$temp=$aa | $bb;
break;
case '^':
$temp=$aa ^ $bb;
break;
default:
exit(__FILE__.__LINE__);
}
$temp=bcmul((string)$temp,$op);
$res=bcadd($res,$temp);
$op=bcmul($op,'65536');
}
return $res;
}