小数の誤差
妹「よく考えると、小数がずれてるなあと思うことは時々あったのだ。ただそういうもんかなあ……と思ってほうっておいたんだけど」
「結論としてはそういうもんなんだけど、一応どういうものか、簡単に説明するよ」
妹「簡単にだぞ、簡単に頼む」
「まず小数の誤差ってのは2種類あって、片方は人間でもやるやつなんだけど、1÷3みたいなのが割り切れないという話。小数で0.333333333…って無限に続くからどこかで切らなきゃいけないし、それに3を掛けても1にはならない。0.999999999みたいになる」
妹「分数にすればいいのでは?」
「それが出来るプログラミング言語はある。UE4でも出来ない事はない。分子と分母を持ったクラスを作って。ただ作るのめんどくさいし、普通の計算より遅くなるしで、ゲームで使ってるって話は聞いたことない」
妹「ふーん、でもまあそれは割り算を使わなければいいのだ。でも前回のやつは足し算と、あと引き算ぐらいしか使ってないよね。それだとずれないと思うんだけど」
「それが2種類目の誤差、今回の主題のやつ。丸め誤差というんだけど……まあこれ実際やってみた方が早いと思う」
「0から始まって0.1ずつ足していくだけのブループリントを作った。DelayとDoOnceは速すぎてよく見えなかったから追加しただけで特に意味はない」
妹「これを実行するとどうなるのだ?」
「こうなる」
妹「なるほど……たしかにズレてる。これ2.8がおかしいの? それとも2.7に0.1を足すとおかしくなるの?」
「どっちでもないよ。さっきのを2.7から始めるとこうなる」
妹「あれ? 今度は2.8が大丈夫で3.3が大丈夫じゃない??? 毎回違うとこが変になるの?」
「そういうことでもない。0から始めたら何度やってもああなるし、2.7から始めたら何度やってもこうなる。Print Stringに表示されないレベルの微妙なズレが計算のたびに発生してて、計算を重ねるうちに違いが大きくなってくる」
妹「さっきのは人間でも起きる誤差と言ってたから、これはパソコンだけで起きる誤差ってこと?」
「うん。パソコンには小数を高速で計算出来る装置みたいのが入ってるんだけど、高速だけど誤差が結構出るんだよ。アンリアルエンジンに限らず起きる問題」
妹「誤差が出ない装置を入れといて欲しかった」
「実はこれも割り切れないのが原因なんだけど、コンピューターの都合だから、割り切れる数字なのかどうか人間が見て判断するのは凄く難しい」
妹「そうするとズレないよう小数に使うってのは無理なのかな」
「整数部分だけ使えば大丈夫。今回のブループリントでいうと1.0ずつ足せばFloat型のままでもズレない。……けどそれだと整数型にした方が早いわけで。それにアクターのLocationなんかは物理演算で勝手に動くから、いつのまにか小数になっちゃう」
妹「どうしようもないのか」
「普段は気にしなくても問題ない。気にしなきゃいけないポイントと、その対処法がわかってればいい」
妹「そのポイントとは!?」
「小数の計算が連続するとしても、それが動いてる物だったらまず問題ない。走ったりとか、弾が飛んだりとか。動くべき方向に動いてるとしか思わない。でも止まってる物はまずい。止まってるのはずのキャラが微妙にずれていったら、おかしいと気づかれちゃう」
妹「そりゃおかしいもん」
「あと数字で表示されるもの。HPとか所持金とか。こういうのはなるべくズレないように計算する必要がある」
妹「ズレないように計算する方法とは?」
「一番いいのは整数しか使わない事。細かく計算したい時は、表示する数字の10倍とか100倍とかの整数で計算する」
妹「そんなことしたら、数字が元よりも増えてしまうのでは」
「途中に点を足せば丁度良い小数に見えるよ」
妹「またそんなテキトーなことを」
「いやいや、普通のゲームで出てくる小数はだいたいこうやって計算してるんだって。これが一番誤差出なくて簡単だから」
妹「あれ点が付いてるだけだったんだ……」
「あと比較も注意。小数は==で比較してはいけない。計算した後はもちろん、代入した直後でも数字によってはズレが起きる、上手く一致しない場合がある。ある程度の誤差が出る前提で、<や>とかでちょっと幅を持たせる必要がある」
妹「うーん、あんまり簡単じゃない気がするなあ」