hnwの日記

PHPのround関数の謎が少し解けた

2週間以上前の記事「PHPの奇妙なround関数」がすごいことになっていますね。最近書き始めたばかりの日記にこんなに人が来るなんて、有名人の集客力は流石だなあ、などと感心しています。


その集客力のおかげかもしれませんが、FreeBSDMac OS Xだと挙動が違うよ、というコメントを頂きました。実際にFreeBSDで試してみたところ、確かにLinuxと異なる、いわばマトモな挙動です。その原因がわかりました、というのが本稿の概要です。僕がモタモタ記事を書いている間に理由がわかっちゃった人も居るかとは思いますし、より詳細なところまで把握した人も居そうですが、僕なりに現時点でわかったことを書いてみます。


前回の記事で、PHP_ROUND_FUZZという定数が「少なくとも僕の手元の環境では」0.50000000001と定義されている、と書きました。この詳細を説明すると、configureスクリプトで下記のコードを実行して、exit codeが1なら0.50000000001に、0なら0.5に定義しています。

#include <math.h>
/* keep this out-of-line to prevent use of gcc inline floor() */
double somefn(double n) {
  return floor(n*pow(10,2) + 0.5);
}
int main() {
  return somefn(0.045)/10.0 != 0.5;
}

僕の手元では0.50000000001になっている、というだけでも面白い事実だと思ったのですが、configure次第だということを書かなかったのはフェアでは無かったかもしれません。これでミスリードされた人も居たかと思います。ごめんなさい。


一方で、僕はこのコードの実行結果は浮動小数点数の四則演算とfloor(3)の挙動にのみ依存していて、少なくともx86系のCPUであれば僕の環境と同じ挙動を示すはずだと考えていました。このコードの挙動がFreeBSDLinuxで違うなどというのは全く想像しませんでしたが、実際のところx86FreeBSD上でconfigureするとPHP_ROUND_FUZZは0.5になります。

続きを読む