丸め処理

四捨五入の処理について。ゲーム(Desigeon)に絡めた話。

まず、ソフトウェアの世界でよく言われる「丸め処理」による四捨五入は、小学校の算数で習う「四捨五入」とは似て非なるものである(必ずしも一致するわけではない)という話から。

SE 市井賢児のメモ - Round() 関数は四捨五入関数ではない
http://ichy.seesaa.net/article/2842259.html

職人以外の方のために補足しておくと、問題となるのは丸める桁が0.5や0.05だった場合の「丸め処理」による四捨五入である。0.5を整数に丸める時は1にするか0にするかという話になる。Round()では偶数優先であるため0になる。しかし、1.5を整数に丸めたときは1か2かという話になって、結果2になる。

まぁ今回はフリーソフトのゲーム(Desigeon)の話だから、目くじらを立てて問題視するほどの仕様ではない。ただし現状のDesigeonは、若干の問題含みのまま動いていることは認識したい。(ゲーム制作者が意識することはそれほどないはずだが)

VBであるDesigeonの編集ツール側では、小数の四捨五入をRound()によって行っている。だから当然、上記のケースが当てはまる。で、当てはまることを確認した。だから入力ボックスに入力した値が四捨五入されるケースでは、「あれ?」と思うことがあるかもしれない。

ところが、その編集データを読み込んで稼働するゲームエンジン側(VC++)では、四捨五入の処理を独自のコードによってまかなっている。こちらは算数で勉強した四捨五入であるため、0.5は1になり、1.5は2になる。

実は、Desigeonにおいてはゲームエンジンの仕様(算数の四捨五入)で動いてもらわないと、アーマークラスの計算に不具合が生じてしまう。具体的には、2レベル単位で1ずつ下がっていくはずの忍者のアーマークラスが、Round()の仕様ではレベルアップしても下がらないことがある。制作当初はまったく意識していなかったが、これは私としては注意しておかねばならない問題である。

今後、編集ツール側ではこの四捨五入のロジックの差が問題となるケースが増えてくるため、どこかで対処したい。


今回の件、恥ずかしながらはっきりと認識しておりませんでした。高校生くらいの時に、プログラム仲間にこういう話をされたことがあったようななかったような。いずれにせよ憶えていなかったので、プログラムに組み込む際も、四捨五入の処理をしっかりテストせずに組み込んでしまいました。

それで、コンピュータでの「丸め処理」が、通常の「四捨五入」よりもいいかどうかは目的次第かなと思いました。四捨五入しながら計算すべきところでは、精度はRound()の方が高いという一般論になるのかと思います。しかし、通常の四捨五入で処理することが明文化されているケースもあるかもしれませんし、そういうケースでは四捨五入の誤差を誤差とみなしていないとも言えますから。

オープン系で不特定多数の人間を相手にするための仕様を決める人にとって、Round()の仕様はいまいち利用者に伝えづらい、と感じるケースは多いのではないかと思います。

よりデジタルな人間にとっては、Round()の方が正しいと認識するでしょうし、実際に恩恵があろうかと思います。できれば私もゲームにはこちらを使いたい。少し不慣れだけど。

まぁ私のように制御系で生きてきた人間にとって、数字の精度に神経を注がねばならない時というのはミリ秒単位以下の時間を扱うときくらいで、小数の精度が問題となるケースはほとんどない、というか整数ばかりを使ってきたから無神経な面があったり。巨大な整数値が7bit-charの並びで"001159430"だとか飛んでくるような環境に触れていると、IEEEだの丸め誤差だのといった概念から切り離されてしまいます。

何にせよ言及元の記事にある通り、大事なのは仕様をはっきりさせることですな……。