Почему при умножении чисел с плавающей запятой ошибок нет?

Мне нужно некоторое уточнение о математике с плавающей запятой.

Я написал некоторый код для обучения purpouses:

#include "stdio.h"

int main (int argc, char const *argv[])
{
    int i;
    double a=1.0/10.0;
    double sum=0;

    for(i = 0; i < 10; ++i)
        sum+=a;

    printf("%.17G\n", 10*a );
    printf("%d\n", (10*a == 1.0) );

    printf("%.17G\n", sum );
    printf("%d\n", (sum == 1.0) );

    return 0;
}

и вывод, который он дает:

    1
    1
    0.99999999999999989
    0

Почему (сумма == 1.0) - ложно, довольно понятно, но почему умножение дает правильный ответ без ошибки?

Спасибо.

Ответ 1

При выполнении повторного добавления происходит 9 округлений: первый результат a+a всегда точно представлен, но после этого дальнейшие дополнения неточны, поскольку показатели базового уровня слагаемых не равны.

При выполнении умножения существует только одно округление, и это дает вам результат, который вы хотели.

Ответ 2

Если вы посмотрите на фактический язык ассемблера, вы обнаружите, что компилятор не генерирует одно умножение, о котором вы просите. Вместо этого он просто предоставляет фактическое значение. Если вы отключите оптимизацию, вы можете получить ожидаемые результаты (если ваш компилятор в любом случае не оптимизирует это).

Ответ 3

У вас много проблем с вашим кодом.

плавающая точка на компьютере - это база 2. Вы не можете точно представлять 0,1 в плавающей запятой (точно так же, как вы не можете представить 1/3 в базе 10, сравнивая яблоки с яблоками), поэтому любые другие предположения после этого (умножьте на 10 и ожидайте это, например, один). Плохо.

Однократное умножение в 10 раз 0.1 только однократно приводит к ошибке. Многократные добавления приводят к ошибке в 10 раз. Округление фиксирует 10 раз 0,1 при преобразовании в целое, что делает его похожим на то, что он действительно работал. Округление является еще одной особенностью плавающей запятой IEEE, режим округления, используемый по умолчанию в вашей системе, а также то, что 1/10-й снова стал снова, в основном сделал однократное умножение, похоже, что оно сработало.

следующая проблема заключается в том, что вы делаете равные сравнения с плавающей точкой, и я предполагаю, что имею какое-то ожидание. dont использовать equals на float, period. Конечно, не с такими цифрами, которые не могут быть представлены точно в плавающей точке.

попробуйте число, например, 1/16th или 1/8th вместо 1/10th...