Проблема точности поплавка
У вас есть две проблемы здесь, но оба происходят из одного корня
Вы не можете сравнивать поплавки точно. Вы не можете вычесть или разделить их точно. Вы не можете рассчитывать ничего для них точно. Любая операция с ними может (и почти всегда) приводит к некоторой ошибке в результате. Даже a=0.2f
не является точной операцией. Более глубокие причины этого очень хорошо объяснены авторами других ответов здесь. (Моя благодарность и голос за них.)
Вот ваша первая и более простая ошибка. Никогда, никогда , никогда , никогда , НИКОГДА не используйте на них == или его эквивалент в любой язык.
Вместо a==b
используйте Abs(a-b)<HighestPossibleError
.
Но это не единственная проблема в вашей задаче.
Abs(1/y-x)<HighestPossibleError
тоже не сработает. По крайней мере, это не будет работать достаточно часто. Зачем?
Давайте возьмем пару х = 1000 и у = 0,001. Давайте возьмем «начальную» относительную ошибку y для 10 -6 .
(Относительная ошибка = ошибка / значение).
Относительные погрешности значений складываются при умножении и делении.
1 / y составляет около 1000. Его относительная ошибка такая же, как 10 -6 . («1» не содержит ошибок)
Это делает абсолютную ошибку = 1000 * 10 -6 = 0,001. Когда вы вычтете x позже, этой ошибкой будет все, что останется. (Абсолютные ошибки добавляются к при сложении и вычитании, а ошибка x пренебрежимо мала.) Конечно, вы не рассчитываете на такие большие ошибки, HighestPossibleError, несомненно, будет установлен ниже, и ваша программа выдаст хорошую пару x у
Итак, следующие два правила для операций с плавающей запятой: старайтесь не делить больший оценщик на меньший, и Бог спасет вас от вычитания близких значений после этого.
Существует два простых способа избежать этой проблемы.
Найдя, что из x, у имеет большее значение abs и делит 1 на большее и только позже вычитает меньшее.
Если вы хотите сравнить 1/y against x
, пока вы еще работаете с буквами, а не со значениями, и ваши операции не дают ошибок , умножьте обе стороны сравнения на y
и у вас есть 1 against x*y
. (Обычно вы должны проверять знаки в этой операции, но здесь мы используем значения abs, поэтому он чистый.) Сравнение результатов вообще не имеет деления.
Короче:
1/y V x <=> y*(1/y) V x*y <=> 1 V x*y
Мы уже знаем, что такое сравнение как 1 against x*y
должно быть сделано так:
const float HighestPossibleError=1e-10;
if(Abs(x*y-1.0)<HighestPossibleError){...
Вот и все.
P.S. Если вам действительно нужно все в одной строке, используйте:
if(Abs(x*y-1.0)<1e-10){...
Но это плохой стиль. Я бы не советовал.
P.P.S. Во втором примере компилятор оптимизирует код так, чтобы он установил z на 5, прежде чем запускать какой-либо код. Таким образом, проверка 5 против 5 работает даже для поплавков.