メインコンテンツまでスキップ

Mathf_Approximately

Mathf.Approximately とは?

Mathf.Approximately は、浮動小数点数(float型)の近似比較を行うUnityの関数です。

浮動小数点数の比較には誤差が伴うため、通常の == 演算子で正確に比較するのが難しい場合があります。Mathf.Approximately は、その誤差を考慮して「ほぼ等しい」と判断するために使われます。


シグネチャ

public static bool Approximately(float a, float b)
  • 引数 ab:
    • 比較したい2つの浮動小数点数。
  • 戻り値:
    • ab が「ほぼ等しい」と判断されれば true を返す。

特徴

  • 浮動小数点数特有の誤差を許容:
    • コンピュータ上では浮動小数点数の演算に微小な誤差が発生します。
    • 例えば、0.1 + 0.2 を計算しても、結果が正確に 0.3 とはならない場合があります。
  • Mathf.Approximately はこの誤差を吸収し、「人間が見る限り等しい」と思える範囲で比較します。

使用例

1. 通常の比較が失敗する例

float a = 0.1f + 0.2f; // 計算結果が厳密には 0.300000012f
float b = 0.3f;

if (a == b) {
Debug.Log("等しい"); // 実行されない
} else {
Debug.Log("等しくない");
}

浮動小数点数の計算誤差により、ab は「等しくない」と判断されます。

2. Mathf.Approximately を使用

float a = 0.1f + 0.2f;
float b = 0.3f;

if (Mathf.Approximately(a, b)) {
Debug.Log("ほぼ等しい"); // 実行される
} else {
Debug.Log("等しくない");
}

Mathf.Approximately を使用すると、ab が「ほぼ等しい」と判断されます。


内部実装(簡略化)

Mathf.Approximately の仕組みは、2つの値の差が小さな閾値(epsilon)以下かどうかを判定しています。

return Mathf.Abs(a - b) < Mathf.Epsilon;
  • Mathf.Abs:
    • 絶対値を求める関数。
  • Mathf.Epsilon:
    • 浮動小数点数で表現できる最小の正の値(非常に小さい値)。
    • 具体的には 1.192092896e-07f(約0.0000001)。

実際のユースケース

1. 小数の比較

UIの移動などで小数点が絡む計算を行う場合、座標や距離が「ほぼ等しいか」を判定する際に使用します。

if (Mathf.Approximately(pos.y, 0f)) {
pos.y = -center.y;
}

上記は、pos.y が0に非常に近い場合に、補正を適用する処理です。

  • 浮動小数点誤差で pos.y が 0.00001 や -0.00001 のような値になる可能性がありますが、これを許容しています。

注意点

  • 誤差が許容される場面で使用する。
  • 非常に大きな値や小さな値(例えば 1e10 や 1e-10)を比較すると、結果が不正確になる場合があります。その場合は自分で適切な閾値を設定した比較を行う必要があります。
bool AreAlmostEqual(float a, float b, float tolerance) {
return Mathf.Abs(a - b) <= tolerance;
}

まとめ

Mathf.Approximately は、浮動小数点の比較を簡略化する便利な関数で、特に以下の場合に適しています:

  • 小数点以下の誤差を気にしない場面。
  • 「ほぼ等しい」かを判断したい場面。





浮動小数点数の誤差が起こる理由

浮動小数点数(floatdouble)の誤差は、コンピュータが数値を2進数で表現する仕組みと、有限のビット数による制約が原因です。


原因1: 数値を2進数で表現する制約

コンピュータは2進数(01)で数値を扱います。しかし、10進数で表現できるすべての数値が、2進数では有限の桁数で正確に表現できない場合があります。

具体例

10進数の 0.1 を2進数で表現するとどうなるか?

  • 10進数の 0.1 は、2進数では 0.00011001100110011...(無限に繰り返す)になります。
  • これをコンピュータは有限のビット数で扱うため、途中で 切り捨て します。

結果として、コンピュータ内部での 0.1厳密な0.1ではなく、少しズレた値 になります。


原因2: 有限のビット数による精度の限界

浮動小数点数は、IEEE 754標準に基づいて表現されます。この形式では、数値を以下のように分解します:

  • 符号部(1ビット):数値が正か負か。
  • 指数部(8ビット or 11ビット):数値のスケール(10の何乗かに相当)。
  • 仮数部(23ビット or 52ビット):実際の数値の細かい部分。

32ビットの float 型では、仮数部が23ビットしかないため、正確に表現できるのは 約7桁の10進数精度 です。

  • それ以上の桁数は丸められるため、小さな誤差が生じます。

原因3: 加算・減算などの演算による累積誤差

浮動小数点数の演算では、結果に誤差がさらに加わる場合があります。

float a = 0.1f;
float b = 0.2f;
float c = a + b; // 結果は厳密な 0.3f ではない
  • 0.10.2 はそれぞれ正確に表現されていないため、足し算の結果もズレます。

誤差の影響が目立つ状況

  1. 繰り返し計算を行う場合

    • 小さな誤差が積み重なり、結果が大きくズレることがあります。
  2. 非常に小さい数値と非常に大きい数値を扱う場合

    • 精度が不均一になるため、丸め誤差が目立ちます。
  3. 数値の比較をする場合

    • 浮動小数点の比較で、想定外の「等しくない」判定が出ることがあります。

解決策や対策

  1. 整数型を使う

    • 誤差が許容できない場合、整数で計算を行い、小数点以下はスケールを使って調整する(例:金額計算で「小数」を使わない)。
    int a = 100; // 0.1 を 100倍として扱う
    int b = 200; // 0.2 を 100倍として扱う
    int c = a + b; // 結果は 300(0.3 を 100倍したもの)
  2. 許容範囲を設けて比較する

    • 誤差を考慮して、数値を「ほぼ等しい」と判定する。
    float tolerance = 0.0001f;
    if (Mathf.Abs(a - b) < tolerance) {
    Debug.Log("ほぼ等しい");
    }
  3. Mathf.Approximately を使う

    • Unityの標準関数を活用する(特にゲーム開発では便利)。
  4. double 型を使用する

    • float より精度が高い。ただし、必要以上に使うとメモリ使用量が増える。

まとめ

浮動小数点数の誤差は、2進数での表現の限界と有限ビット数による制約から生じます。この誤差は避けられませんが、許容範囲を設けたり整数型を使用することで多くの場面で対策可能です。

ゲーム開発では、Mathf.Approximately や許容誤差を使った比較が特に重要です!