Ответ
Прямое сравнение вещественных чисел через == ненадежно из-за ошибок округления при представлении в памяти. Стандартный подход — сравнение с допустимой абсолютной погрешностью (epsilon).
#include <cmath>
bool almostEqual(double a, double b, double epsilon = 1e-9) {
return std::fabs(a - b) < epsilon;
}
Почему этого может быть недостаточно? Абсолютный epsilon плохо работает для чисел разного масштаба (например, сравнение 1e-10 и 2e-10). В таких случаях используют относительную погрешность.
bool relativeEqual(double a, double b, double relEpsilon = 1e-9) {
double maxVal = std::max(std::fabs(a), std::fabs(b));
// Защита от деления на ноль при сравнении с нулём
if (maxVal < relEpsilon) return true;
return std::fabs(a - b) / maxVal < relEpsilon;
}
Практические советы:
- Для финансовых расчётов используйте целочисленные типы (счёт в копейках/центах) или библиотеки с десятичной арифметикой.
- В C++11 появились
std::nextafterиstd::nexttowardдля сравнения с учётом соседних представимых чисел. - Библиотеки вроде Google Test (
ASSERT_NEAR,ASSERT_DOUBLE_EQ) реализуют комбинированный подход (Ульп-тест), проверяющий и абсолютную, и относительную разницу.