Это то, что меня укусило тоже.
Да, числа с плавающей точкой никогда не должны сравниваться на равенство из-за ошибки округления, и вы, вероятно, знали это.
Но в этом случае вы вычисляете t1+t2
, а затем вычисляете его снова. Неужели что должно дать идентичный результат?
Вот что, вероятно, происходит. Держу пари, что вы используете это на процессоре x86, правильно? FPU x86 использует 80 бит для своих внутренних регистров, но значения в памяти хранятся как 64-битные двойные числа.
Итак, t1+t2
сначала вычисляется с точностью 80 битов, затем - я полагаю - сохраняется в памяти в sum_2
с точностью 64 бита - и происходит некоторое округление. Для утверждения он загружается обратно в регистр с плавающей запятой, и t1+t2
вычисляется снова, снова с 80 битами точности. Итак, теперь вы сравниваете sum_2
, который ранее был округлен до 64-битного значения с плавающей запятой, с t1+t2
, который был вычислен с более высокой точностью (80 бит) - и поэтому значения не совсем идентичны .
Редактировать Так почему первый тест проходит? В этом случае компилятор, вероятно, оценивает 4.0+6.3
во время компиляции и сохраняет его как 64-битную величину - как для назначения, так и для утверждения. Таким образом, сравниваются идентичные значения, и утверждение проходит.
Второе редактирование Вот код сборки, сгенерированный для второй части кода (gcc, x86), с комментариями - в значительной степени соответствует сценарию, описанному выше:
// t1 = 4.0
fldl LC3
fstpl -16(%ebp)
// t2 = 6.3
fldl LC4
fstpl -24(%ebp)
// sum_2 = t1+t2
fldl -16(%ebp)
faddl -24(%ebp)
fstpl -32(%ebp)
// Compute t1+t2 again
fldl -16(%ebp)
faddl -24(%ebp)
// Load sum_2 from memory and compare
fldl -32(%ebp)
fxch %st(1)
fucompp
Интересное примечание: это было скомпилировано без оптимизации. Когда он скомпилирован с -O3
, компилятор оптимизирует весь кода.