hnwの日記

round関数その7:偶数丸め

いい加減まとめるか他の記事書けよって感じですが、まだまだround関数の続きです。実際のところ僕も結構飽きていたりします。


この一連のround関数の件について言及している記事を見ると、かなり怪しい知識レベルの人もいるように思います。そんな記事をいちいち斬っていてもキリが無いかなと思ったのですが、下記の内容を信じていそうな人を見かけました*1。なので渋々ツッコミを入れます。

なお、round関数は、四捨五入じゃないのはシステム業界では有名なお話。

「偶数丸め」は IEEE 754 で定められており、JIS や ISO にも同じ規定があります。

多くの Round() 関数の挙動もこの IEEE 754 に則った「偶数丸め」です。

「偶数丸め」は四捨五入とほぼ同一ですが、次の1点が違います。

「丸め単位の丁度まんなかで、どっちつかずの場合は、偶数側を採用する」

したがって、1.25 を 0.01 の位で丸めると 1.2 になり、1.35 は 1.4 になります。

まっちゃだいふくの日記★とれんどふりーく★

PHPをはじめround関数が偶数丸めになっている言語は僕の周囲には殆ど見当たりません。少なくともRubyPythonは違います。ついでに言えばC(というかC99)も違いますし、JavaJavaScriptのMath.roundも違います。どうすればシステム業界一般論のように思えるんでしょうか。それともCやJavaは既に少数派なんですかね?百歩譲って「MS環境では比較的一般的」という話題だとしても無関係なマメ知識で、並べて書くのはミスリードを誘っているとしか思えません。


仮にUn*x界隈からやや遠い人だとしても、たとえば[ruby-list:36917]以降の議論*2を見れば、PythonPHPも違うよ、なんて話題が見つかりますし、他の言語についても少し検索すれば簡単に見つかります。もしくは、RubyPythonのround関数についてのソースコードを確認するだけでも一目瞭然な話ですし、「man 3 round」が打てる環境を持っていないわけでも無いでしょう。


更に、この引用部自体どうかと思います。この文章は元記事を短く再構成しているものなんですけど、元の文章の後半部に前半部の例をつないだせいで、1.25と1.35の話がIEEE754浮動小数点数の丸めの話に見えかねない文章に生まれ変わっています。1.25はまだしも1.35を2進の浮動小数点数でピッタリ表すことは不可能ですが、どういう意味で「丸め単位の丁度真ん中」になると言うのでしょうか。1.35であれば不正確な理解でも結果は同じになりますけど、たとえばdoubleの4.0925と4.0935を小数点以下第3位までに丸めた場合にはどうでしょうか*3。元の文章を書いた人は一定以上理解していると思いますが、再構成した人の知識レベルについては疑問ですね。元の記事を256回読んだ方がいいんじゃないでしょうか。


僕の日々の目標の一つは、世の中にあふれている「知識」を自分の血肉としての「知恵」へと変えていくことです*4。知恵を得ることは誰にでもできることで、自分の手を動かし、自分の頭を働かせるだけのことです。知恵を身につけることを怠ってコピペだけの記事を乱発したり、あまつさえ劣化コピーをバラ巻くような人物は、僕の価値判断基準で言えば負の価値を生んでいます*5


僕がこういう記事を書くと皮肉だらけで自分でもどうかと思うことがあるので、できるだけ控えたいと思っています。心ある人が端から優しくツッコミを入れていってくれれば世界が平和になるのかもしれませんが、僕には無理です。



追記1: 蛇足かもしれませんが、僕らが普段使っているCPUはIEEE754準拠であり、内部的に偶数丸めを行っています。これはどういうことかというと、たとえば54bit目が1で以降のbitが全部0であるような計算結果を仮数部52bit(hidden bitを含めると53bit)の倍精度に丸める、などという場合の話です。CPUがIEEE754の丸めを実装していてデフォルトの丸めモードが偶数丸めだったとしても*6、round関数の挙動は各言語で個別に実装しないと偶数丸めにはなりません。このあたりを混同している人も多少居そうに思いましたので、念のため。



追記2: Round関数が偶数丸めな言語ということで、C#でRound関数の挙動を調べるプログラムを適当に書いてみました。キャプチャして置いておきますけど、なんだか少し変な箇所がありますよね。理屈はわかりきっているわけですが*7、僕も徐々に飽きてきているので、ネタにしたい奇特な方がもしいれば解説をお願いします。




追記3:「1.25は2進小数でピッタリ表せるかもしれないけれども、1.2と1.3がピッタリ表せない以上、丁度真ん中という概念に大した意味が無いのでは?」といった意図の御指摘をある方からメールで頂きました(読み返してみると私が意図を勘違いしているような気もしてきましたが)。ご指摘ありがとうございます、確かにそんな気もします。根本的な話題として、そもそも2進で表現している小数を10進表記での特定の桁で丸めるということ自体が不思議な話なんですよね。意図を勘違いしていたようなので一端撤回します。

*1:この記事が原因ではなくて、何人かが独立に同じことを考えただけかもしれませんが

*2:偶然にもMatzさんがPHPのround関数のソースコードをチェックしています。この頃はPHPPythonRubyと全く同じ実装だったわけですが

*3:本文最後、追記2を参照のこと

*4:羽生 善治『決断力』からのパクリです。彼が言い始めたことでも無いとは思いますが

*5:自戒の意味も込めて。上記記事の人も悪意があったわけでは無いと思いますが、記事を書くのにもう少し時間を使ったらどうかと思います

*6:普通はそのはずですが

*7:C#自体のソースコードを見たわけではないので想像ですけど