Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
3
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

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

浮動小数点数の比較

このような話が話題になりました。さて、今回の記事では、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 のようです。
また、絶対誤差か相対誤差、どちらか一方でも条件を満たせば、真と判定するようです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
3
Help us understand the problem. What are the problem?