第51回PHP勉強会@関東に参加して、15分ほどのプレゼンをしました。勉強会の会場は、毎度おなじみトライコーン株式会社さんでした。suzukiさん、会場提供&ust配信ありがとうございました。そして幹事のgusagiさん毎度ありがとうございます。
- 発表資料 : 「64bit PHPの罠」(PDF、少し改訂版)
- Ustream録画:http://www.ustream.tv/recorded/5746520
内容としては、2年ほど前のmaru_ccさんの記事「php5.2.6からstrtotime関数の挙動が変わる - maru.cc@はてな」とかぶる内容ですが、自分の周りで64bit環境が増えてきたので改めてまとめなおした感じです。
懇親会などで質問をもらったので、何点か補足してみます。
負のunix timeの扱い
unix timeというのは通常1970年1月1日からの経過秒を指しますが、負の方向への拡張は容易です。PHP 5.0.5までは負のunix timeは環境依存でした。つまり、PHPの実装としてCライブラリのmktime(3)を利用していたため、負のunix timeを扱える環境と扱えない環境とありました。
PHP5.1.0以降、PHPはこの部分を自前実装して、負のunix timeが公式にサポートされました。最近の32bit環境であれば、PHPは1901年末から2038年頭までunix timeで扱えるというわけです。
僕が以前会社ブログに書いた記事でもこのあたりに触れています。
0000-00-00撲滅運動
プレゼン中に口答で軽く説明しましたが、MySQLは年月日のそれぞれを独立に0にできます。「2010-03-00」のように「年月日のどれかが未入力」を記録できる分だけNULLよりも表現力はあるのですが、混乱の元ですし、SQL標準にも従っていないわけで、使わないのがいいと僕は考えています。
このあたり、会社ブログで2回記事を書いています。あわせてご覧ください。
intとfloatの精度について
intとfloatの表せる値について、数直線で説明してみます。まずは32bit整数の上限値(2^31-1)付近での数直線を描いてみました。
数直線で表すと、浮動小数点数は0に近いほど目盛りが細かくなります。浮動小数点数については理科の実験で出てきたような有効数字つきの表現を考えるとわかりやすいかもしれません。
- 1.2345 x 10^8
例えば上の数と同じノリで考えると、IEEE64bit浮動小数点数は下記のように表現できます。
- (53bit整数) x 2^n
つまり2^31付近であれば浮動小数点数の精度は十分すぎるわけで、このあたりの数であればfloatにキャストしても大して不安はないと思うんですね。
一方、64bitのPHP_INT_MAX(2^63-1)付近を数直線で表すと次のようになります。
つまり、64bit整数の最大値付近を扱うには、64bit浮動小数点数の精度では足りないということです。とはいえ、不正確になるだけで、数としてはずっと大きい値(最大値は10^308程度)まで扱えますから、intの範囲を超えたら勝手にfloatになる仕様は十分合理的だと思います。
64bit環境だとCのintもdoubleも64bitで、ついに追いつかれちゃったというような話をしましたが、誤解を招く表現だったかもしれません。IEEE 64bit浮動小数点数について言うと、仮数部のサイズ53bitがついにintに追い越された、というのが正確な表現です。そのためPHP_INT_MAX付近ではintを正確に扱えない、ちょっとキモい気がする、ってことが言いたかったんですが、準備不足で端折りすぎました。ごめんなさい。
プレゼンの補足のまとめ
- 0000-00-00は禁止したい!
- 64bit環境でintからfloatにキャストしてもそれほど問題ないと思う
- ナイスなバグを見つけたらタレコミお願いします