Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Погрешность при вычислениях


Автор: Burka 6.5.2016, 12:41
Здравствуйте, я работаю над задачей где нужно вычислять большие значения геометрической прогрессии. Проблема в том что расчет по формуле расходится с расчетами в цикле, накапливается погрешность. Непонятно что правильней считает.  Подскажите как разобраться, что правильней.

http://cpp.sh/4uwzi

Первый вариант
Код

double f1(double count, double base, double power)
{
    double Bn = base * pow(power, count - 1);
    double Sn = (base * (1 - pow(power, count))) / (1 - power);
    return Sn;
};


Второй вариант
Код

double f2(double count, double base, double power)
{
    double result = base;
    double current_price = base;

    for (int i = 1; i < int(count); i++)
    {
        current_price *= power;
        result += current_price;
    }

    return result;
}


разница значений выглядит примерно так
Цитата

[0]  -2
[1]  0
[2]  0
[3]  1.77636e-15
[4]  0
[5]  0
[6]  0
[7]  0
[8]  0
[9]  0
[10]  0
[11]  -7.10543e-15
[12]  0
[13]  -1.42109e-14
[14]  -1.42109e-14
[15]  0
[16]  -1.42109e-14
[17]  0
[18]  -2.84217e-14
[19]  -2.84217e-14
[20]  0
[21]  -2.84217e-14
[22]  0
[23]  -5.68434e-14
[24]  0
[25]  5.68434e-14
[26]  5.68434e-14

Автор: feodorv 7.5.2016, 13:53
Оба правильны (если не рассматривать случай count == 0). Расхождение имеет смысл нормировать (то есть выводить не абсолютную ошибку, а относительную).

Автор: Burka 7.5.2016, 14:32
Ну этого не может быть потому что при больших числах погрешность сильно увеличивается

Цитата

[287]  -512
[288]  -1024
[289]  -1024
[290]  -1024
[291]  0
[292]  -1024

а это уже существенно. А точнее 300 это даже не большие еще значения

Автор: feodorv 7.5.2016, 15:35
Цитата(Burka @  7.5.2016,  14:32 Найти цитируемый пост)
при больших числах погрешность сильно увеличивается

Относительная ошибка какая?

Добавлено через 2 минуты и 7 секунд
Если Вам так важна точность, имеет смысл копать в сторону специализированных библиотек, поддерживающих желаемую точность. Но и скорость вычисления тогда упадет значительно.

Автор: Burka 8.5.2016, 12:01
В общем мне надо выбрать из двух этих функций какая точнее считает, видимо из за того что погрешность накапливается то дело в цикле, формула должна считать точнее. Но вот как проверить это пока не понял

Автор: baldman88 8.5.2016, 14:43
Один из самых простых вариантов -- взять какую-нибудь числодробилку и сравнить результаты.

Автор: NoNo 10.5.2016, 10:22
У меня в универе, однажды проблема была с погрешностью вычислений...(не помню уже задачу, которая была)..помню только то, что проблема заключалась в сокращенных записях операций умножения/деления и.т.д. с присвоением над вещ. числами(у тебя второй вариант). Я даже тогда проводил тесты этого дела(правда сейчас этого не осталось). Так что я предлагаю использовать первый вариант.
P.S. предлагаю тебе прлтестировать твою задачу.....взять второй вариант как есть....и тоже самое, но без сокращенных операций...т.е. заменить  *= на ...=..*.. и проверить результаты сходятся в оом числе и с первым вариантом

Автор: Alexeis 10.5.2016, 11:20
В данном случае правильно сказали, что число double не имеет достаточно разрядности для того, чтобы правильно посчитать такие значения. Так при 
f1(288, 2, 1.15);
получается значение ~ -605 352 050 046 196 131, для точного хранения которого необходимо 59 битное число, тогда как число double имеет всего 52 бита. Т.е. сразу видно, что 7 младших разрядов отсекаются. Это не говоря уже про то, что каждая операция с double числами имеет свою погрешность. В случае цикла мы имеем 288 умножений и столько же сложений. Погрешность даже одной операции довольно значительная. Как минимум последняя десятичная цифра в числе считается неверно. Это 3-4 двоичных разряда. Даже если учесть только эти погрешности, то мы имеем 7 разрядов потерянных при отсечении и в лучшем случае 3 при умножении. Т.е. 10 разрядов - 2^10 = 1024 . Такая вот беглая оценка показывает, что расхождение в 1024 на 288м элементе вполне допустима для чисел double. 
  Если ваш компилятор поддерживает long double c 63 битной точностью и сопроцессор будет настроен на вычисления с этой точностью, то для количества элементов 288 расхождения опять будут минимальными.

Добавлено через 3 минуты и 52 секунды
Собственно даже гадать нечего, online компилятор поддерживает long double http://cpp.sh/5udj

Автор: Burka 10.5.2016, 12:44
Alexeis, cпасибо за подробный ответ

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)