Почему GCC дает неожиданный результат при добавлении значений с плавающей запятой? - PullRequest
3 голосов
/ 22 июля 2011

Я использую GCC для компиляции программы, которая добавляет плавающие, длинные, целые и символы.Когда он работает, результат плохой.Следующая программа неожиданно печатает значение 34032.101562.

Перекомпиляция с помощью компилятора Microsoft дает правильный результат.

#include <stdio.h>

int main (void) {
    const char val_c = 10;
    const int val_i = 20;
    const long val_l = 34000;
    const float val_f = 2.1;

    float result;
    result = val_c + val_i + val_l + val_f;

    printf("%f\n", result);
    return 0;
}

Ответы [ 4 ]

13 голосов
/ 22 июля 2011

Как вы думаете, «правильный результат»? Я предполагаю, что вы верите, что это 34032,1. Это не так.

2.1 не представляется как float, поэтому val_f вместо этого инициализируется с ближайшим представимым значением float. В двоичном коде 2.1 это:

10.000110011001100110011001100110011001100110011001...

a float имеет 24 двоичных разряда, поэтому значение val_f в двоичном виде:

10.0001100110011001100110

Выражение resultat = val_c + val_i + val_l + val_f вычисляет 34030 + val_f, которое вычисляется с одинарной точностью и вызывает другое округление.

  1000010011101110.0
+               10.0001100110011001100110
-----------------------------------------
  1000010011110000.0001100110011001100110
rounds to 24 digits:
-----------------------------------------
  1000010011110000.00011010

В десятичном виде этот результат в точности равен 34032.1015625. Поскольку формат %f печатает 6 цифр после десятичной точки (если не указано иное), он снова округляется, и printf печатает 34032.101562.

Теперь, почему вы не получаете этот результат при компиляции с MSVC? Стандарты C и C ++ позволяют выполнять вычисления с плавающей точкой в ​​более широком типе, если компилятор решит это сделать. MSVC делает это с вашими вычислениями, что означает, что результат 34030 + val_f не округляется перед передачей в printf. В этом случае точное значение с плавающей точкой для печати равно 34032.099999999991268850862979888916015625, которое округляется до 34032.1 с помощью printf.

Почему не все компиляторы делают то, что делает MSVC? Несколько причин. Во-первых, это медленнее на некоторых процессорах. Во-вторых, и что более важно, хотя он может дать более точные ответы, программист не может зависеть от этого - казалось бы, несвязанные изменения кода могут вызвать изменение ответа при наличии такого поведения. Из-за этого дополнительная точность часто вызывает больше проблем, чем решает.

2 голосов
/ 23 июля 2011

Google Статья Дэвида Голдберга "Что должен знать каждый компьютерщик об арифметике с плавающей точкой".

1 голос
/ 22 июля 2011

Формат float имеет точность порядка 6-7 цифр.Используйте % 7.1f или другой приемлемый формат, и вам больше понравятся ваши результаты.

0 голосов
/ 22 июля 2011

Я не вижу здесь никаких проблем.2.1 не имеет точного представления в формате IEEE с плавающей запятой, и поэтому конвертирует весь ответ в число с плавающей запятой примерно с 6-7 (правильными) контрольными цифрами.Если вам нужно больше точности, используйте двойной.

...