C ++ Ошибка числового усечения - PullRequest
7 голосов
/ 07 мая 2010

извините, если глуп, но не смог найти ответ.

#include <iostream>

using namespace std;

int main()
{
double a(0);
double b(0.001);
cout << a - 0.0 << endl;
for (;a<1.0;a+=b);
cout << a - 1.0 << endl;
for (;a<10.0;a+=b);
cout << a - 10.0 << endl;
cout << a - 10.0-b << endl;
return 0;
}

Выход:
0
6.66134e-16
0,001
-1.03583e-13

Попробовал скомпилировать его с MSVC9, MSVC10, Borland C ++ 2010. Все они в итоге приходят с ошибкой около 1e-13. Нормально ли иметь такое значительное накопление ошибок только с шагом 1000, 10000?

Ответы [ 4 ]

13 голосов
/ 07 мая 2010

Да, это обычная ошибка числового представления с плавающей точкой. Это связано с тем, что аппаратное обеспечение должно аппроксимировать большинство чисел с плавающей запятой, а не хранить их точно. Таким образом, используемый вами компилятор не должен иметь значения.

Что должен знать каждый компьютерщик об арифметике с плавающей точкой

2 голосов
/ 07 мая 2010

подумай об этом.каждая операция вносит небольшую ошибку, но следующая операция использует слегка ошибочный результат.при достаточном количестве итераций вы будете отклоняться от истинного результата.если хотите, напишите свои выражения в форме t0 = (t + y + e), t1 = (t0 + y +e) и определите термины с помощью epsilon.из их условий вы можете оценить приблизительную ошибку.

есть и второй источник ошибки: в какой-то момент вы объединяете относительно небольшие и относительно большие числа к концу.Если вы помните определение точности станка, 1 + e = 1, в какой-то момент операции будут терять значимые биты.

Надеюсь, это поможет уточнить в терминах неспециалистов

2 голосов
/ 07 мая 2010

Вот почему при использовании ошибки с плавающей запятой вы никогда не должны делать:

if( foo == 0.0 ){
    //code here
}

и вместо этого

bool checkFloat(float _input, float _compare, float _epsilon){
    return ( _input + _epsilon > _compare ) && ( _input - _epsilon < _compare );
}
1 голос
/ 07 мая 2010

Это проблема с числами с плавающей точкой - они приблизительны, и странные вещи происходят в нуле (то есть появляются странные представления).Из-за этого некоторые операции с числами, которые вы принимаете как должное, нужно обрабатывать более деликатно.

При сравнении двух чисел вы не можете просто сказать a == b, потому что одно из них может быть 0 идругие -1.03583e-13 из-за потери точности по операциям с плавающей запятой, применяемым для получения a и b.Вы должны выбрать произвольный допуск, например: fabs(a,b) < 1e-8.

При печати номера вам часто нужно ограничить количество напечатанных цифр.Если вы используете printf, вы можете сказать printf("%g\n", a);, что не будет печатать такие вещи, как -1.03583e-13.Я не знаю, есть ли iostream аналог %g;там есть?

...