LoginSignup
7
3

More than 5 years have passed since last update.

浮動小数点数の比較で許容する相対誤差と絶対誤差

Last updated at Posted at 2017-12-15

浮動小数点数の比較

このような話が話題になりました。さて、今回の記事では、D言語でなぜこのような結果になるかについて…はお話しません

浮動小数点数を == で比較するのは禁じ手もいいところですので、誤差範囲を決めたうえで比較を行うのが定石というものです。
D言語では std.math.approxEqual を使用します。

import std.math;
auto a = 3.0 - 2.0;
auto b = 2.0 - 1.0;
auto c = a.approxEqual(b);
writeln(c);

さて、このapploxEqualですが、誤差範囲を指定することができます。
指定の仕方は、比較対象に続けて、相対誤差、絶対誤差を指定できます。

import std.math;
auto a = 3.0 - 2.0;
auto b = 2.0 - 1.0;
auto c = a.approxEqual(b, 0.0001, 0.001);
writeln(c);

関数の定義は以下のようになっています。
公式ドキュメント

/** Params:
 *      lhs        = 値1
 *      rhs        = 値2
 *      maxRelDiff = rhsに対して許容される最大の相対誤差
 *      maxAbsDiff = 最大の絶対誤差
 */
bool approxEqual(T, U, V)(T lhs, U rhs, V maxRelDiff, V maxAbsDiff = 1e-05);

ところで、この相対誤差と絶対誤差って、なんのことなのでしょうか。

相対誤差と絶対誤差

一般的には、相対誤差と絶対誤差は、以下のように定義されます。

絶対誤差 = 値 - 真値

相対誤差 = 絶対誤差 / 真値

ここで、値と真値というのが出てきますが、そもそも「誤差」っていうのは、「こうあるべき」「基準となる」「本当の値」っていうようなものがあって、それに対してどれだけずれがあるか、というものですので、基準があるはずなんですね。

approxEqualの挙動

approxEqualでは、lhsとrhsで、どのようにして絶対誤差と相対誤差を出しているのでしょうか。基準となっているのはlhsとrhsどちらなのでしょう。
さらに、絶対誤差の範囲内に入っていて、相対誤差では範囲外だとどうなるのでしょうか。その逆は?

以下、実験です。

import std.math, std.stdio;

void main()
{
    real x = 100;
    real y = 100.1;
    // 絶対誤差
    writeln(abs(x - y)); // -> 0.1
    // (xを真値とした)相対誤差1
    writeln(abs(x - y) / x); // -> 0.001
    // (yを真値とした)相対誤差2
    writeln(abs(x - y) / y); // -> 0.000999
    // 相対誤差はNG、絶対誤差はNG
    writeln(approxEqual(x, y, 0.0001, 0.001)); // -> false
    // 相対誤差はOK、絶対誤差はNG
    writeln(approxEqual(x, y, 0.002, 0.001)); // -> true
    // 相対誤差はNG、絶対誤差はOK
    writeln(approxEqual(x, y, 0.0001, 0.2)); // -> true
    // 相対誤差1はNG、相対誤差2はOK、絶対誤差はNG
    writeln(approxEqual(x, y, 0.0009995, 0.001)); // -> true

    x = 100.1;
    y = 100;
    // 絶対誤差
    writeln(abs(x - y)); // -> 0.1
    // 相対誤差1
    writeln(abs(x - y) / x); // -> 0.000999
    // 相対誤差2
    writeln(abs(x - y) / y); // -> 0.001
    // 相対誤差はNG、絶対誤差はNG
    writeln(approxEqual(x, y, 0.0001, 0.001)); // -> false
    // 相対誤差はOK、絶対誤差はNG
    writeln(approxEqual(x, y, 0.002, 0.001)); // -> true
    // 相対誤差はNG、絶対誤差はOK
    writeln(approxEqual(x, y, 0.0001, 0.2)); // -> true
    // 相対誤差1はOK、相対誤差2はNG、絶対誤差はNG
    writeln(approxEqual(x, y, 0.0009995, 0.001)); // -> false
}

上記の結果から分かる通り、相対誤差で真値(基準)としているのは、 rhs のようです。
また、絶対誤差か相対誤差、どちらか一方でも条件を満たせば、真と判定するようです。

7
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3