C++ Числовая ошибка усечения

извините, если немой, но не мог бы найти ответ.

#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 инкрементов?

7
задан Andrew 7 May 2010 в 01:23
поделиться

4 ответа

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

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

13
ответ дан 6 December 2019 в 10:48
поделиться

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

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

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

1
ответ дан 6 December 2019 в 10:48
поделиться

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

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

Надеюсь, это поможет прояснить термины непрофессионала

2
ответ дан 6 December 2019 в 10:48
поделиться

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

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

, а вместо этого делать

bool checkFloat(float _input, float _compare, float _epsilon){
    return ( _input + _epsilon > _compare ) && ( _input - _epsilon < _compare );
}
2
ответ дан 6 December 2019 в 10:48
поделиться
Другие вопросы по тегам:

Похожие вопросы: