Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

浮動小数点数の比較

このような話が話題になりました。さて、今回の記事では、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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした