C - Суммирование двойных переменных дает разные выходные данные в зависимости от того, как они написаны - PullRequest
0 голосов
/ 20 октября 2019

У меня есть эти переменные:

double a = 5;
double b = 1E20;
double c = -b;

, и я суммировал их так:

double temp = a+b;
double result = temp+c;

Результат равен 0, как и ожидалось, потому что «b» - большое число по сравнениюна «а», и как таковой, результат ничем не отличается от числа b, и вычитая его с «с», который такой же, как «б», но отрицательный, дает нам 0. Однако, если я попробую этопуть:

double result = (a+b)+c;

Результат на самом деле 8. Почему это так?

1 Ответ

2 голосов
/ 20 октября 2019

Предположительно, вы запускаете эту программу на процессоре Intel. (Задавая подобные вопросы, вы всегда должны указывать, какой компилятор вы используете, включая версию и параметры командной строки, а также на какой системе вы запускаете программу.) Процессоры Intel имеют 80-битный формат с плавающей запятой, которыйимеет 64-битные значения. (Значение и является дробной частью числа с плавающей запятой.)

Похоже, что ваш компилятор использует 80-битный формат с плавающей запятой процессора для промежуточных вычислений, и он, вероятно, использует базовый IEEE-75464-битный двоичный формат для double. Стандарт C позволяет реализациям C оценивать выражения с плавающей точкой с большей дальностью и точностью, чем номинальный тип. Это означает, что когда компилятор оценивает (или генерирует код для оценки) выражение double, ему разрешается использовать 80-битный тип.

Когда объекту присваивается выражение с плавающей запятой илиСуществует явное приведение к типу с плавающей точкой, стандарт C требует, чтобы реализация C «отбрасывала» избыточную точность.

Вышеприведенное позволяет нам увидеть, что произошло. 1e20 представляет 10 20 , что является числом от 2 66 до 2 67 . Записанный в двоичном виде, его ведущий бит находится в позиции для значения 2 66 . Поскольку 80-битный формат имеет 64-битные значения, наименее значимый бит, который может быть представлен в формате, находится в позиции 2 3 (число битов от 3 до 66 равно 64 битам). После b = 1e20, когда вы добавляете 5 к b, результат должен быть округлен до битов от 2 66 до 2 3 (что равно 8). Это приводит к округлению числа до следующего, кратного 8. Таким образом, из-за округления, b+5 имеет тот же результат, что и b+8. Затем, когда вы добавляете c, что равно -b, вы получаете 8.

В double temp = a+b;, назначение заставляет реализацию C «отбрасывать» избыточную точность. Таким образом, он должен преобразовать результат в формат double, который имеет 53-битные значения. С начальным битом 2 66 младший бит равен 2 14 . Биты от 2 13 до 2 3 отбрасываются, а оставшиеся биты округляются (что не приводит к каким-либо изменениям в этом случае, поскольку отброшенные биты оказываются меньше, чемсредняя точка). Таким образом, хотя a+b равно b+8, как мы видели выше, результат преобразования b+8 в double равен просто b. Затем, добавив c к этому, вы получите 0.

...