Ответ
Прямое сравнение вещественных чисел через == ненадежно из-за ошибок округления при представлении в памяти. Стандартный подход — сравнение с допустимой абсолютной погрешностью (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) реализуют комбинированный подход (Ульп-тест), проверяющий и абсолютную, и относительную разницу.
Ответ 18+ 🔞
Ёпта, ну смотри, тут такая история с этими вещественными числами — просто пиздец, а не тема. Берёшь два double, сравниваешь через == и думаешь, что всё окей, а потом бац — и нихуя не равно, хотя по логике должно быть. А всё потому, что в памяти они представляются с какими-то ебенями, с ошибками округления, короче, полный ёперный театр.
Вот стандартный костыль, который все используют — сравнение с какой-то погрешностью, эпсилон там.
#include <cmath>
bool almostEqual(double a, double b, double epsilon = 1e-9) {
return std::fabs(a - b) < epsilon;
}
Но тут, чувак, подвох есть, и он жирный. Этот метод — хуй с горы, если числа разного масштаба. Ну представь: сравниваешь 1e-10 и 2e-10. Разница-то копеечная, но абсолютный эпсилон в 1e-9 её просто не заметит, скажет "равно", а по факту-то одно число в два раза больше другого! Вот это пиздопроебибна, да?
Поэтому умные дядьки придумали сравнивать ещё и относительно, через относительную погрешность.
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;
}
А теперь практика, без неё нихуя:
- Если пишешь что-то для денег — для финансовых расчётов — выкинь нахуй эти
doubleсразу. Бери целые числа, считай в копейках, центах, сатоши, не еби мозг. Или бери спецбиблиотеки для десятичной арифметики, там всё чётко. - В C++11, кстати, подвезли
std::nextafterиstd::nexttoward— ими можно проверять, являются ли числа соседними представимыми значениями, это тоже вариант, но мозг надо включать. - Самый, бля, безбашенный и правильный способ — не изобретать велосипед. Возьми Google Test, там есть
ASSERT_NEARиASSERT_DOUBLE_EQ. Они внутри используют комбинированный подход (этот ваш Ульп-тест), который проверяет и абсолютную, и относительную разницу — и волнение ебать отпадает. Просто пользуйся и не парься.