Страницы

Поиск по вопросам

среда, 18 декабря 2019 г.

Точность при вычислении

#c


Решаю олимпиадные задачи различного рода, когда работал в Паскале никогда и не задумывался
над работой оператора"=" или ">=" в работе с вещественными числами, я думал, что с
точностью сравнений и проблем не может быть. Но вот я перешел на C++ и один учитель
мне сказал, что "==" или ">=" нельзя использовать при работе с вещественными числами
в Си, нужно писать fabs(a - b) > eps. И вот вопрос:

Правда ли это?
Как правильно подобрать eps?
И почему же могли некоторые задачи не проходить все тесты с такой фишкой, а когда
писал просто "==" они проходили.

Вот конкретный пример. Задача, которая прошла после того, как я изменил LessEqual
на "<=". Дано действительное число a и натуральное n. Вычислите корень n-й степени
из числа a. Программа должна вывести единственное число: ответ на задачу с точностью
не менее 6 знаков после запятой
#include 
#include 
#include 

using namespace std;
const double eps = 1e-8;
const double eps2 = 1e-12;

bool Equal(double a, double b)
{
    return fabs(a - b) < eps2;
}

bool lessEqual(double a, double b)
{
    return ((a < b) || Equal(a, b));
}

double a;
int n;

void solve()
{
    double l = 0,
           r = a + 1,
           mid = 0;
    while (r - l > eps)
    {
        mid = (l + r) / 2;
        if (lessEqual(pow(mid, n), a))
            l = mid;
        else
            r = mid;
    }
    printf("%.7lf", l);
}

int main()
{
    freopen("input.txt", "r", stdin);
    freopen("output.txt", "w", stdout);

    scanf("%lf\n", &a);
    scanf("%d", &n);
    solve();

    return 0;
}
    


Ответы

Ответ 1



Действительно ничего хорошего из сравнения типа == при операциях с вещественными числами не выйдет. Можно попробовать простой код: double sum = 0; for ( int i = 0; i++; i < 1000000000 ) { sum += 0.000000001 } Гарантировать, что в итоге sum будет равным 1 в данном случаи невозможно. Может быть равен, а может и нет. Дело в машинной точности округления мат. операций. И ваш преподаватель все правильно говорит. Сравнение вида fabs ( 1.0 - sum ) < 0.0000000000000001; Даст верный результат всегда. ps Число 0.0000000000000001 взял просто так, но поверьте, такая ошибка вычислений очень мала, но все же точного результата ( sum == 1.0 ) вы не получите

Ответ 2



Проблема здесь не в "неточном" сравнении аппаратурой процессора, а в том, что некоторые десятичные дроби не могут быть точно преобразованы в двоичный формат чисел с плавающей точкой. Еще одна проблема заключается в потере младших разрядов мантиссы при вычислениях в циклах.

Ответ 3



В развитие ответа уважаемого @AVP. Если задача позволяет, преобразуйте свои числа к дробям, которые могут быть представлены двоичной дробью: не 0.1, а, например, 25/256 и т.д. Это обезопасит Ваши циклы от недохода или превышения заданной величины постоянной цикла. В С++ определена интересующая Вас константа epsilon в библиотеке float.h - DBL_EPSILON. Посмотрите ее значение именно для Вашего компилятора. Сравнение ">=" - всегда правомерно.

Ответ 4



Не совсем верно. eps зависит от того, насколько точно вы хотите посчитать те или иные показатели. Обычно точность указывается при постановке задачи. Например: Посчитать с точностью до седьмого знака после запятой, тогда eps = 0.0000001 Тут непонятно, нужно смотреть задачи. А вообще, точность достаточно условное понятие, как можно видеть из формулы fabs(a - b) > eps, и от задаче к задаче она будет меняться.

Ответ 5



На самом деле, в реальной жизни нельзя, например, сказать что две палки равны по длине, можно сказать что их длины различаются на очень небольшую величину, т.е. |длина1-длина2|

Комментариев нет:

Отправить комментарий