извините, если немой, но не мог бы найти ответ.
#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, 10 000 инкрементов?
Да, это обычная ошибка числового представления с плавающей запятой. Это связано с тем, что оборудование должно аппроксимировать большинство чисел с плавающей запятой, а не хранить их точно. Таким образом, используемый вами компилятор не имеет значения.
Что должен знать каждый компьютерный ученый об арифметике с плавающей запятой
Это проблема чисел с плавающей запятой - они приблизительны, и при нуле происходят странные вещи (то есть, появляются странные представления). Из-за этого некоторые операции над числами, которые вы считаете само собой разумеющимися, должны выполняться более деликатно.
Сравнивая два числа, вы не можете просто сказать a == b
, потому что одно может быть 0
, а другое -1.03583e-13
из-за потери точности при операциях с плавающей запятой, применяемых для получения a
и b
. Вы должны выбрать произвольный допуск, например, такой: fabs(a,b) < 1e-8
.
При печати числа часто требуется ограничить количество печатаемых цифр. Если вы используете printf
, вы можете сказать printf("%g\n", a);
, что не позволит напечатать что-то вроде -1.03583e-13
. Я не знаю, существует ли iostream
аналог %g
; есть ли он?
подумайте об этом. каждая операция вносит небольшую ошибку, но следующая операция использует слегка ошибочный результат. при достаточном количестве итераций вы отклонитесь от истинного результата. если хотите, запишите выражения в виде t0 = (t + y + e), t1 = (t0 + y + e)
и вычислите термины с помощью эпсилона. по их срокам можно приблизительно оценить погрешность.
есть и второй источник ошибок: в какой-то момент вы комбинируете относительно небольшие и относительно большие числа ближе к концу. Если вы вспомните определение машинной точности, 1 + e = 1
, в какой-то момент операции будут терять значащие биты.
Надеюсь, это поможет прояснить термины непрофессионала
Вот почему при использовании ошибки с плавающей запятой никогда не следует делать:
if( foo == 0.0 ){
//code here
}
, а вместо этого делать
bool checkFloat(float _input, float _compare, float _epsilon){
return ( _input + _epsilon > _compare ) && ( _input - _epsilon < _compare );
}