#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|
Комментариев нет:
Отправить комментарий