浮動小数点数の比較(丸め誤差に注意)

2019年9月15日

10進数を2進数に変換することにより、誤差が生じます。
プログラムで比較する場合、このことを考慮しないと同一値と判断されないことがあります。

テストコード

using System;
using UnityEngine;

public class FloatTest : MonoBehaviour
{
    void Start()
    {
        Func<int, float, bool> Check;

        Debug.Log("floatの誤差");
        Check = (i, f) => i == f * 10.0f;
        CompareCalc(Check);

        Debug.Log("floatの誤差(誤差の小ささで調整)");
        Check = (i, f) => Mathf.Abs(i - f * 10.0f) < 0.001f;
        CompareCalc(Check);

        Debug.Log("floatの誤差(Equalsメソッドでチェック)");
        Check = (i, f) => (f * 10.0f).Equals(i);
        CompareCalc(Check);


        Func<int, decimal, bool> CheckDeciaml;


        Debug.Log("floatの誤差(decimal型で比較)");
        CheckDeciaml = (i, d) => i == d * 10.0m;
        CompareCalc(CheckDeciaml);

        Debug.Log("floatの誤差(decimal型で比較(Equalsメソッドでチェック))");
        CheckDeciaml = (i, d) => (d * 10.0m).Equals(i);
        CompareCalc(CheckDeciaml);

    }

    /// <summary>
    /// 0から10までの整数を元に、float型で、10で割った結果を10倍して比較
    /// </summary>
    /// <param name="compareFunc">Compare func.</param>
    private static void CompareCalc(Func<int, float, bool> compareFunc)
    {
        float data = 0.0f;

        for (int i = 0; i < 10; i++)
        {
            string compare;

            if (compareFunc(i, data))
            {
                compare = "同じ";
            }
            else
            {
                compare = "違う";
            }

            Debug.Log(i + ":" + compare);

            data += 0.1f;
        }

        Debug.Log("");
    }

    /// <summary>
    /// 0から10までの整数を元に、decimal型で、10で割った結果を10倍して比較
    /// </summary>
    /// <param name="compareFunc">Compare func.</param>
    private static void CompareCalc(Func<int, decimal, bool> compareFunc)
    {
        decimal data = 0;

        for (int i = 0; i < 10; i++)
        {
            string compare;

            if (compareFunc(i, data))
            {
                compare = "同じ";
            }
            else
            {
                compare = "違う";
            }

            Debug.Log(i + ":" + compare);

            data += 0.1m;
        }

        Debug.Log("");
    }
}

テストコード(LINQ版)

using System;
using System.Linq;
using UnityEngine;

public class FloatTest : MonoBehaviour
{
    void Start()
    {
        Func<int, float, bool> Check;

        Debug.Log("floatの誤差");
        Check = (i, f) => i == f * 10.0f;
        CompareCalc(Check);

        Debug.Log("floatの誤差(誤差の小ささで調整)");
        Check = (i, f) => Mathf.Abs(i - f * 10.0f) < 0.001f;
        CompareCalc(Check);

        Debug.Log("floatの誤差(Equalsメソッドでチェック)");
        Check = (i, f) => (f * 10.0f).Equals(i);
        CompareCalc(Check);

        Func<int, decimal, bool> CheckDeciaml;

        Debug.Log("floatの誤差(decimal型で比較)");
        CheckDeciaml = (i, d) => i == d * 10.0m;
        CompareCalc(CheckDeciaml);

        Debug.Log("floatの誤差(decimal型で比較(Equalsメソッドでチェック))");
        CheckDeciaml = (i, d) => (d * 10.0m).Equals(i);
        CompareCalc(CheckDeciaml);

    }

    /// <summary>
    /// 0から10までの整数を元に、float型で、10で割った結果を10倍して比較
    /// </summary>
    /// <param name="compareFunc">Compare func.</param>
    private static void CompareCalc(Func<int, float, bool> compareFunc)
    {
        Enumerable.Range(0, 10).ToList().ForEach(i => Debug.Log(compareFunc(i, (float)i / 10) ? $"{i} : 同じ" : $"{i} : 違う"));

        Debug.Log("");
    }

    /// <summary>
    /// 0から10までの整数を元に、decimal型で、10で割った結果を10倍して比較
    /// </summary>
    /// <param name="compareFunc">Compare func.</param>
    private static void CompareCalc(Func<int, decimal, bool> compareFunc)
    {
        Enumerable.Range(0, 10).ToList().ForEach(i => Debug.Log(compareFunc(i, (decimal)i / 10) ? $"{i} : 同じ" : $"{i} : 違う"));

        Debug.Log("");
    }
}

実行結果

floatの誤差
0 : 同じ
1 : 違う
2 : 違う
3 : 違う
4 : 違う
5 : 同じ
6 : 違う
7 : 違う
8 : 違う
9 : 違う

floatの誤差(誤差の小ささで調整)
0 : 同じ
1 : 同じ
2 : 同じ
3 : 同じ
4 : 同じ
5 : 同じ
6 : 同じ
7 : 同じ
8 : 同じ
9 : 同じ

floatの誤差(Equalsメソッドでチェック)
0 : 同じ
1 : 同じ
2 : 同じ
3 : 同じ
4 : 同じ
5 : 同じ
6 : 同じ
7 : 同じ
8 : 同じ
9 : 同じ

floatの誤差(decimal型で比較)
0 : 同じ
1 : 同じ
2 : 同じ
3 : 同じ
4 : 同じ
5 : 同じ
6 : 同じ
7 : 同じ
8 : 同じ
9 : 同じ

floatの誤差(decimal型で比較(Equalsメソッドでチェック))
0 : 同じ
1 : 同じ
2 : 同じ
3 : 同じ
4 : 同じ
5 : 同じ
6 : 同じ
7 : 同じ
8 : 同じ
9 : 同じ

C#,Unity

Posted by hidepon