Почему результат отличается, когда формула одинакова? - PullRequest
2 голосов
/ 28 февраля 2020
int y = 89;
int foo =  (y / 10.0 - y / 10) * 10;
int bar = (89 / 10.0 - 89 / 10) * 10;
cout << foo << ' ' << bar << '\n';

Приведенный выше код предназначен для получения последнего di git целого числа y, странно то, что foo равно 8, а bar * 9, почему это происходит? В чем разница между двумя версиями выражений?

1 Ответ

5 голосов
/ 28 февраля 2020

Стандарт C ++ позволяет реализации оценивать выражения с плавающей точкой с большей точностью, чем это требуется для номинального формата. Например, выражения float могут оцениваться, как если бы они были double или более, а выражения double могут оцениваться, как если бы они были long double. Эта дополнительная точность может вызвать различия в оценке, особенно когда используются прерывистые функции (например, преобразование в int).

Например, если y = 89, y / 10.0 - y / 10 будет 0,9 в действительном числе арифметическое c, но 0,9000000000000003552713678800500929355621337890625 в double (бинарный код IEEE-754) арифметическое c и 0,899999999999999999653055304804638581117615103721618 * * * 10 * с префиксом 10 *, 10 *, преобразование в 10-битное форматирование с ускорением в 10-битном формате, с разрешением 10-го и 10-го форматов, в том числе в 10-битном формате с разрешением 10-го и 10-го форматов, в том числе в форматах: 1012 * производит 9 или 8 соответственно.

При отключенной оптимизации компилятор может вычислять выражение с y во время выполнения и выражение с 89 во время компиляции, и он может использовать разные точность для них. При оптимизации компилятор, вероятно, распознает y, фактически являющуюся константой 89 в первом выражении, и оценивает оба выражения во время компиляции, используя один и тот же метод для обоих.

Стандарт C ++ требует, чтобы приведение и Операции присваивания преобразуются в номинальный тип, поэтому одним из тестов, чтобы увидеть, происходит ли это, является вставка приведений:

int foo = (double) ((double) (y / 10.0) - y / 10) * 10;
int bar = (double) ((double) (89 / 10.0) - 89 / 10) * 10;

Если это приводит к идентичности foo и bar, что поддерживает гипотезу. У вашего компилятора могут быть переключатели для управления оценкой выражений с плавающей точкой.

Еще один тест - включить <cfloat> и вывести значение FLT_EVAL_METHOD. Если оно равно 0, реализация утверждает, что оценивает операции с плавающей запятой в их номинальном типе, и такое поведение не должно происходить. Если оно равно 1 или 2, реализация утверждает, что использует double или long double для оценки double выражений соответственно, и такое поведение снова не должно происходить, поскольку оба выражения будут оцениваться одинаково. Если это -1, реализация не делает эти утверждения, и поведение может произойти.

Чтобы получить последний di git неотрицательного целого числа, используйте y % 10.

...