Независимо от стандарта C ++, такие несоответствия встречаются на практике в различных настройках.
Существует два примера, которые легко вызвать:
Для 32-битной x86 не все так хорошо. Добро пожаловать в g cc номер ошибки 323 , из-за которого 32-разрядные приложения не соответствуют стандарту. Что происходит, так это то, что регистры с плавающей запятой в x86 имеют 80 бит, независимо от типа в программе (C, C ++ или Fortran). Это означает, что следующее обычно сравнивает 80-битные значения, а не 64-битные:
bool foo(double x, double y)
{
// comparing 80 bits, despite sizeof(double) == 8, i.e., 64 bits
return x == y;
}
Это не будет большой проблемой, если g cc может гарантировать, что double
всегда принимает 80 бит. К сожалению, число регистров с плавающей запятой конечно, и иногда значение сохраняется в памяти. Таким образом, для тех же x и y x==y
может быть оценено как true
после разлива в память и false
без разлива в память. Нет никаких гарантий относительно (отсутствия) утечки в память. Поведение изменяется, по-видимому, случайным образом в зависимости от флагов компиляции и, по-видимому, несущественных изменений кода.
Таким образом, даже если x и y должны быть логически равны, а x
становится разлитым, тогда x == y
может оцените как false
, так как y
содержит бит 1
в его младшем значащем бите мантиссы, но x
урезал этот бит из-за разлива. Тогда ответ на ваш второй вопрос: x ==y
может возвращать разные результаты в разных местах, в зависимости от разлива или отсутствия в 32-битной программе x86.
Аналогично, x >= y
может возвращать true
, даже если у должен быть немного больше, чем x
. Это может произойти, если после разлива в 64-битную переменную в памяти значения станут равными. В этом случае, если ранее в коде x > y || x == y
вычисляется без разлива в память, он будет оцениваться как false
. Чтобы сделать вещи более запутанными, замена одного выражения на другое может привести к тому, что компилятор сгенерирует немного другой код с разной потерей памяти. Разница в разливе для двух выражений может привести к непоследовательному различию результатов.
Та же проблема может возникнуть в любой системе, где операции с плавающей запятой выполняются с разной шириной (например, 80 бит для 32 бит x86) чем то, что хочет код (64 бита). Единственный способ обойти это несоответствие - заставить разлив после каждой операции с плавающей запятой усечь превышение точности. Большинство программистов не заботятся об этом из-за снижения производительности.
Второй случай, который может вызвать несоответствия , - это небезопасные оптимизации компилятора. По умолчанию многие коммерческие компиляторы выбрасывают согласованность FP из окна, чтобы получить несколько процентов времени выполнения. Компилятор может решить изменить порядок операций FP, даже если они могут давать разные результаты. Например:
v1 = (x + y) + z;
v2 = x + (y + z);
bool b = (v1 == v2);
Понятно, что скорее всего v1 != v2
, из-за разного округления. Например, если x == -y
, y > 1e100
и z == 1
, то v1 == 1
, но v2 == 0
. Если компилятор слишком агрессивен, он может просто подумать об алгебре и сделать вывод, что b
должно быть true
, даже не оценивая ничего. Это то, что происходит при запуске gcc -ffast-math
.
Вот пример , который показывает это.
Такое поведение может сделать x == y
несовместимым и в значительной степени зависеть от того, что компилятор может вывести в конкретном c фрагменте кода.