web-dev-qa-db-ja.com

2つのdouble値のc ++比較が適切に機能しない

このコードを見てください:

_#include <cmath>
#include <iostream>
using namespace std;
class Sphere
{
    double r;
public:
    double V() const { return (4/3) * 3.14 * pow(r,3); }
    bool equal(const Sphere& s) const
    {
        cout  << V() << " == " << s.V() << " : " << ( V() == s.V() );
        return ( V() == s.V() );

    }

    explicit Sphere(double rr = 1): r(rr){}

};
main()
{
    Sphere s(3);
    s.equal(s);
}
_

出力は_84.78 == 84.78 : 0_です。これは、すべてのパラメーターが静的であっても、同じメソッドが毎回同じ値を返さないことを意味しますか?

しかし、次のようにV()メソッド定義で_3.0_の代わりに_3.14_と書くと:

_double V() const { return (4/3) * 3.0 * pow(r,3); }
_

次に、出力は次のとおりです。_84.78 == 84.78 : 1_

ここで何が起こっていますか?私のプログラムでは、2つのオブジェクトのボリュームを比較するこのメソッドが必要ですが、不可能です。問題の原因を突き止めるために長い間頭を叩きましたが、幸いなことにそれを見つけましたが、今はその理由がわかりません。コンパイラ(GCC)と関係があるのですか、それともここで重要な何かを見逃していますか?

14
tuks

==演算子を使用して浮動小数点値を比較すると、エラーが発生しやすくなります。 shouldが等しい2つの値は、算術丸め誤差が原因ではない可能性があります。これらを比較する一般的な方法は、イプシロンを使用することです:

bool double_equals(double a, double b, double epsilon = 0.001)
{
    return std::abs(a - b) < epsilon;
}
25
nijansen

浮動小数点比較には2つの問題があります。

(1)浮動小数点演算には、通常、予測が困難な少なくとも小さな丸め誤差が伴います。したがって、数学的に同じ結果を与えるはずの2つの浮動小数点演算(4.7 *(1.0/3.14)と4.7/3.14など)は異なる結果をもたらす可能性があります。

(2)コンパイラは、必要以上に高い精度で浮動小数点演算を実行できる場合があります。また、他のときに必要だった精度で、まったく同じ浮動小数点演算を行うこともできます。したがって、まったく同じ操作でも、結果がわずかに異なる場合があります。

OPの問題を解決するために、これは(2)が原因であるように見えます。コンパイラが必要以上に高い精度を使用するのを防ぐことができるコンパイラオプションがあるかどうかを見つけようとします。

4
gnasher729