Unity で「情報落ち」を再現してみる


情報落ち

浮動小数点数では、大きな数と小さな数の足し算を行うと結果として小さな数が足されなかったことが起きます。

 c = a + b // a:Large Number ,b:Small Number
 c == a // True  (??)

Unity で再現してみる

[Github] note-nota/InformationLoss

カメラの前に四角い箱があってクルクル回るシーン。
Cube には下記のスクリプトがアタッチされています。

RotaionError.cs
        void Update()
        {
            rotation_deg += speed_deg * Time.deltaTime;
            transform.rotation = Quaternion.Euler(0f, rotation_deg, 0f);
        }

これにより、y 軸周りの回転だけがスムーズに行われる…と思いきや、あるとき回転が止まる。
speed_deg を大きくするとまた動き出します。

説明してみる

先人の知恵を拝借して、少しばかり説明してみる。

浮動小数点型では「指数部」と「仮数部」に分けて実数を表現する。

float\mbox{ の表す値} = (-1)^{符号部} × 2^{指数部-127} × 1.\mbox{ 仮数部}

float であれば 4 バイト(32 ビット)であり、2のべき乗以下の数を表現する仮数部は 23 ビットの精度でしか表現できない。$2^{23} = 8,388,608$ なので、おおむね7桁程度の精度しかもっていないことになる。そのため、7桁以上の大きな開きがある足し算をするとこの仮数部では表現できない、という状況が発生します。これが、「情報落ち」。

今回の Unity の例では、rotation_deg が非常に大きな値 (1.677 e+07) のため、足す値 speed_deg * Time.deltaTime が $\mathcal{O}_{(1)}$ 程度で「足し算の結果、足した値が反映されていない!」状況になります。