С++ сравнение двух двойных значений не работает должным образом

Посмотрите на этот код:

#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 означает, что один и тот же метод не возвращает одно и то же значение каждый раз, хотя все параметры являются статическими?

Но если я пишу 3.0 вместо 3.14 в определении метода V(), например:

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

Затем вывод: 84.78 == 84.78 : 1

Что здесь происходит? Мне нужен этот метод, для моей программы, который будет сравнивать объемы двух объектов, но это невозможно? Я так долго ударил головой, чтобы выяснить, в чем причина проблемы, и, к счастью, я ее нашел, но теперь я не понимаю, почему? Имеет ли это какое-то отношение к компилятору (GCC), или я пропустил что-то важное здесь?

Ответ 1

Сравнение значений с плавающей запятой с использованием оператора == очень подвержено ошибкам; два значения, которые должны быть равными, могут быть не связаны с ошибками арифметического округления. Общим способом их сравнения является использование epsilon:

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

Ответ 2

Есть две проблемы с сравнениями с плавающей запятой:

(1) Операции с плавающей запятой обычно включают как минимум крошечные ошибки округления, которые трудно предсказать. Поэтому две операции с плавающей запятой, которые должны математически давать одинаковый результат (например, 4.7 * (1.0/3.14) против 4.7/3.14), могут давать разные результаты.

(2) Компилятору разрешено выполнять операции с плавающей запятой, иногда с большей точностью, чем необходимо. Также разрешено выполнять точные операции с плавающей запятой только с той точностью, которая была необходима в другое время. Поэтому точно такая же операция может дать несколько иные результаты, что вы видите здесь.

Чтобы решить проблему OP, это выглядит так, как будто это вызвано (2). Я попытаюсь найти, есть ли какие-либо параметры компилятора, которые могут помешать компилятору использовать более высокую точность, чем это необходимо.