経緯
仕事でPHPで0xから始まる16進数を符号付きの10進数に変換する必要があったが、ネットで探してもなかなか見つからなかったため備忘録として記載。
結論
//16進数を符号付き10進数に変換する
private function hex_to_decimal($val)
{
//0x表記の場合
if (strpos($val,"0x") === 0) {
//「0x」の箇所を削除する
$hex = substr($val,2);
}
//先頭文字の1ビット目が1→マイナス
if (hexdec(substr($hex,0,1)) & 8 && strlen($hex) === 8) {
//2の補数で絶対値を取得
$hex = (hexdec($hex) ^ (16 ** strlen($hex)-1)) + 1;
//マイナスに変換
$dec = 0 - $hex;
} else {
//先頭文字の1ビット目が0→プラス
$dec = hexdec($hex);
}
return $dec;
}
解説
符号付16進数はビット列の最初の値で正負の判断をしている。
・先頭ビットが1→負
・先頭ビットが0→正
例として負の値-1(16進数:0xFFFFFFFF)で解説。
まずは、下記の箇所
if (hexdec(substr($hex,0,1)) & 8) {
hexdec(substr($hex,0,1))で16進数の最初の文字「F」の10進数(=15)を取得。
15(2進数で1111)と8(2進数で1000)のビット積を求めることで、最初のビットが1か0かを判定することができる。(&はビット積の意)
最初のビットが1の場合は負の値のため、下記の処理に移る。
$hex = (hexdec($hex) ^ (16 ** strlen($hex)-1)) + 1;
ここでは元の16進数(0xFFFFFFFF)と32ビット分全て"1"の数値を排他的論理和でビット演算を行い、その数値にプラス1をすることで2の補数で負の値の絶対値を取得している。
その後に、下記の計算で負の値に変換して終了。
$dec = 0 - $hex;
最後に
もっといい方法あったら教えてください!