Здесь есть несколько постов о числах с плавающей запятой и их природе.Ясно, что сравнение чисел с плавающей запятой и удвоений всегда должно выполняться осторожно. Просьба о равенстве также обсуждалась, и рекомендация явно избегать этого.
Но что, если существует прямое назначение:
double a = 5.4;
double b = a;
assumg a
- это любое не- NaN значение - может ли a == b
когда-либо быть ложным?
Кажется, что ответ, очевидно, нет, но я не могу найти никакого стандарта, определяющего это поведение в среде C ++. IEEE-754 утверждает, что два числа с плавающей запятой с одинаковыми (не NaN) шаблонами битов равны .Значит ли это, что я могу продолжать сравнивать свои двойники, не беспокоясь о ремонтопригодности?Должен ли я беспокоиться о других компиляторах / операционных системах и их реализации в отношении этих строк?Или, может быть, компилятор, который оптимизирует некоторые биты и разрушает их равенство?
Я написал небольшую программу, которая генерирует и сравнивает случайные числа, отличные от NaN, навсегда - пока не найдет случай, когда a == b
дает false
.Могу ли я скомпилировать / запустить этот код в любом месте и в любое время в будущем, не ожидая остановки?(игнорируя порядковый номер и предполагая, что размеры / позиции битов знака, экспоненты и битов мантиссы остаются прежними).
#include <iostream>
#include <random>
struct double_content {
std::uint64_t mantissa : 52;
std::uint64_t exponent : 11;
std::uint64_t sign : 1;
};
static_assert(sizeof(double) == sizeof(double_content), "must be equal");
void set_double(double& n, std::uint64_t sign, std::uint64_t exponent, std::uint64_t mantissa) {
double_content convert;
memcpy(&convert, &n, sizeof(double));
convert.sign = sign;
convert.exponent = exponent;
convert.mantissa = mantissa;
memcpy(&n, &convert, sizeof(double_content));
}
void print_double(double& n) {
double_content convert;
memcpy(&convert, &n, sizeof(double));
std::cout << "sign: " << convert.sign << ", exponent: " << convert.exponent << ", mantissa: " << convert.mantissa << " --- " << n << '\n';
}
int main() {
std::random_device rd;
std::mt19937_64 engine(rd());
std::uniform_int_distribution<std::uint64_t> mantissa_distribution(0ull, (1ull << 52) - 1);
std::uniform_int_distribution<std::uint64_t> exponent_distribution(0ull, (1ull << 11) - 1);
std::uniform_int_distribution<std::uint64_t> sign_distribution(0ull, 1ull);
double a = 0.0;
double b = 0.0;
bool found = false;
while (!found){
auto sign = sign_distribution(engine);
auto exponent = exponent_distribution(engine);
auto mantissa = mantissa_distribution(engine);
//re-assign exponent for NaN cases
if (mantissa) {
while (exponent == (1ull << 11) - 1) {
exponent = exponent_distribution(engine);
}
}
//force -0.0 to be 0.0
if (mantissa == 0u && exponent == 0u) {
sign = 0u;
}
set_double(a, sign, exponent, mantissa);
b = a;
//here could be more (unmodifying) code to delay the next comparison
if (b != a) { //not equal!
print_double(a);
print_double(b);
found = true;
}
}
}
с использованием Visual Studio Community 2017 Version 15.9.5