さて、またまた「PHPの奇妙なround関数」の続きです。今回はタイトルからいきなり負けそうですけど、気にせずいきましょう。
Matzさんの記事に反論するにはかなり理論武装が必要だろうという考えから、ここ2回の記事は僕としても頑張って書いてきました。ただ、相当数の方が議論についてきていないのではないか、ということに気づきました*1。このページは僕が面白いと思ったことを他人に伝えるのが目的のはずなのに、あまり伝わらないようでは意味がなくなってしまいます。また、できれば面白さを伝えたいと思っているのですが、僕に余裕が無い内容を扱っていると伝えることさえ難しかったりします*2。
また、そもそも理論武装が必要な話になってたっけ?というと、そんなことはないんですよね。僕の想像ではMatzさんは感覚的なことを言っているだけで、具体的にこういうアプリケーションで害があるという話ではないように思います。一方で、どんなユーザーが来ても文句を言わせないような完璧な言語を作る、というのはとても理解できる感覚です。自分なら絶対そういう実装にしない、という意見表明に対しては全く文句がありません。素晴らしいことだと思います。
ただ、一点だけ言っておくべきことがあるとも思います。
えーと、通常なら絶対にあり得ない設計なんだが、 PHPユーザはこういう設計センスの言語を信頼できるのか*2。
なんだかとても悲しい気持ちになった。
Matzにっき(2007-05-28)
なかなか理系らしからぬ内容ですよね。要するに「こんな実装があるんじゃPHPには他にも問題が隠れているかもしれない」と解釈して良いのでしょうか。なるほど、そうかもしれませんし、そうじゃないかもしれません。Matzさんはこんな理系っぽくない発言で釣られるようなユーザーが欲しいんでしょうか。逆に失望する人も居そうですけど。
煽りあうのが目的ではないですから、あまりここから広げるのはやめておきます。僕が言いたいのは一点、釣りや煽りは上品な趣味じゃないですよ、ってことです。Rubyにはいいところがたくさんあるわけで、それを宣伝するのがいい大人ってものじゃないでしょうか。
さて、僕もPHPの対応は格好悪いとは思います。それは否定しませんけど、格好悪いことが問題なわけではないと思います。僕がこの対応について問題だと思った点は3点です。
- こういう中途半端な数で実装した理由がどこからも読み取れない
- こういう実装にしたことがマニュアルに書いていない
- こんな(ある意味で親切な)実装にしたら初心者が浮動小数の罠にハマれなくなってしまう!それは教育的によろしくない!*3
こういう挙動の関数なんだよ、ということがマニュアルに書いてあれば問題が無いと僕は思っていますが、何か具体的な問題点を指摘できる方がいらっしゃるんでしょうか。
PHPが定義する(こともある)例の「0.50000000001」という数を見直してみましょう。round関数でこの程度の誤差が入ったとしても、有効数字として考えると10進換算で11桁目かそれ以降の桁に1を足すことになります*4。つまり、有効桁数がそれ以下のデータを扱っている範囲においては、この実装の影響を受けることはありません。例えば32ビット整数(最大で10進10桁)同士の除算程度であれば、どうやってもこの実装の影響は受けません。受けないというと不正確かもしれませんが、こうした有理数を浮動小数として表した際に0.5から0.00000000001以内の距離にあったとすれば、それは確実に0.5を意味します。*5
また、IEEE64ビット浮動小数点数の有効数字は10進で言えば16桁程度です。10進16桁より先はアバウトでもよくて、12桁あたりでいい加減だと困るアプリケーションって何でしょう?それはもしかして10進16桁でも不十分なアプリケーションだったりしませんか?もしそうであれば、僕が2回目の記事で紹介した「GMP関数」や「BCMath任意精度数学関数」などを使うべきです。これは他の言語でも同様だろうと思います。
この一連の話題は基本的には少し笑って終わるような小ネタのつもりでした*6。そこにMatzさんが「キモい」という感想を言っただけで注目されてしまったわけですけど、なんでこんなに注目されているのか本当に不思議です。
実は、最初の記事中にまだ訂正していない嘘が1点残っています。それもこの祭りを加速させてしまった理由の一つかもしれません。それは次回の話題ということで。
また、頑張ってPHPを擁護してみたものの、これがベストな実装なのかというとそれも違う気がします。これについての議論もまだ書いていく必要がありそうです。
そろそろ他の小ネタを書いて息抜きしたいんですけど、しばらくはこのネタに振り回されそうですね。ご意見・反論などお待ちしております。
*1:コメント欄やその他で助けて頂いたりして、書いている僕にとっても背伸びした内容でした
*2:僕自身の勉強という意味では大変面白かったのは確かですけど
*3:これは他の場所でいくらでもハマれますから、最初の記事を書いた時点での僕の勘違いです
*4:0.5付近のときのみ11桁、1.5から先は12桁目より先ということになります。わかりやすさのために10進で説明しているのでアバウトですが
*5:わかりやすさのために有効数字の話を導入してみたのですが、論法としては失敗だったかもしれません。この話題は次回以降に再構成する必要がありそうです。論法に問題があるとしてもここで結論としている話題は真実で、i/j(iもjもint)が0.5以外で0.5から0.00000000001以内の距離に存在することはありえません。
*6:ここ2回は変に真面目ぶっていましたけど