Общепринятое мнение, что числа с плавающей точкой не могут сравниваться на равенство, неверно. Числа с плавающей запятой ничем не отличаются от целых чисел: если вы оцените «a == b», вы получите истину, если они будут идентичными числами, а в противном случае - ложными (при том понимании, что два NaN, конечно, не являются одинаковыми числами).
Реальная проблема заключается в следующем: если я выполнил некоторые вычисления и не уверен, что два числа, которые я должен сравнить, точно верны, тогда что? Эта проблема одинакова для чисел с плавающей запятой и целых чисел. Если вы оцените целочисленное выражение «7/3 * 3», оно не будет сравниваться с «7 * 3/3».
Итак, предположим, мы спросили: «Как сравнить целые числа на равенство?» в такой ситуации. Там нет однозначного ответа; что вы должны сделать, зависит от конкретной ситуации, в частности от того, какие у вас есть ошибки и чего вы хотите достичь.
Вот несколько возможных вариантов.
Если вы хотите получить «истинный» результат, если математически точные числа будут равны, то вы можете попытаться использовать свойства вычислений, которые вы выполняете, чтобы доказать, что вы получаете одинаковые ошибки в двух числах. Если это выполнимо, и вы сравниваете два числа, которые являются результатом выражений, которые дали бы равные числа, если вычислить точно, то вы получите «true» из сравнения Другой подход заключается в том, что вы можете проанализировать свойства вычислений и доказать, что ошибка никогда не превышает определенную величину, возможно, абсолютную величину или величину по отношению к одному из входов или одному из выходов. В этом случае вы можете спросить, отличаются ли два вычисленных числа не более чем на эту сумму, и вернуть «true», если они находятся в пределах интервала. Если вы не можете доказать ошибку, вы можете догадаться и надеяться на лучшее. Один из способов угадать - оценить множество случайных выборок и посмотреть, какое распределение вы получите в результатах.
Конечно, поскольку мы устанавливаем требование, чтобы вы получали «true», если математически точные результаты равны, мы оставили открытой возможность того, что вы получите «true», даже если они неравны. (Фактически, мы можем удовлетворить требование, всегда возвращая «true». Это делает расчет простым, но, как правило, нежелательным, поэтому я расскажу об улучшении ситуации ниже.)
Если вы хотите получить «ложный» результат, если математически точные числа будут неравными, вам нужно доказать, что ваша оценка чисел дает разные числа, если математически точные числа будут неравными. Это может быть невозможно для практических целей во многих общих ситуациях. Итак, давайте рассмотрим альтернативу.
Полезным требованием может быть получение «ложного» результата, если математически точные числа отличаются более чем на определенную величину. Например, возможно, мы собираемся вычислить, куда попал мяч, брошенный в компьютерной игре, и мы хотим знать, ударил ли он по летучей мыши. В этом случае мы, безусловно, хотим получить «истину», если мяч ударяет по бите, и мы хотим получить «ложь», если мяч находится далеко от летучей мыши, и мы можем принять неверный «истинный» ответ, если мяч математически точное моделирование пропустило летучую мышь, но находится в миллиметре от удара по ней. В этом случае нам нужно доказать (или угадать / оценить), что наши расчеты положения шара и положения летучей мыши имеют суммарную ошибку не более одного миллиметра (для всех интересующих позиций). Это позволило бы нам всегда возвращать «ложь», если мяч и летучая мышь находятся на расстоянии более миллиметра друг от друга, возвращать «истину», если они касаются, и возвращать «истину», если они достаточно близки, чтобы быть приемлемыми.
Итак, то, как вы решите, что возвращать при сравнении чисел с плавающей запятой, очень сильно зависит от вашей конкретной ситуации.
Что касается того, как вы можете доказать границы ошибок для расчетов, это может быть сложным вопросом.Любая реализация с плавающей запятой, использующая стандарт IEEE 754 в режиме округления до ближайшего, возвращает число с плавающей запятой, ближайшее к точному результату для любой базовой операции (в частности, умножение, деление, сложение, вычитание, квадратный корень).(В случае связывания округлите, чтобы младший бит был четным.) (Будьте особенно осторожны с квадратным корнем и делением; ваша языковая реализация может использовать методы, которые не соответствуют IEEE 754 для них.) Из-за этого требования мы знаемошибка в одном результате составляет не более 1/2 от значения младшего значащего бита.(Если бы это было больше, округление дошло бы до другого числа, которое находится в пределах 1/2 от значения.)
Идти оттуда становится значительно сложнее;Следующий шаг - выполнение операции, когда на одном из входов уже есть какая-то ошибка.Для простых выражений за этими ошибками можно проследить вычисления, чтобы достичь границы окончательной ошибки.На практике это делается только в нескольких ситуациях, например, при работе с высококачественной математикой.И, конечно же, вам необходимо точно контролировать, какие именно операции выполняются.Языки высокого уровня часто дают компилятору большую слабость, поэтому вы можете не знать, в каком порядке выполняются операции.
Существует гораздо больше того, что можно (и есть) написать по этому вопросу, но у меня естьостановиться там.Таким образом, ответ таков: для этого сравнения нет библиотечной подпрограммы, потому что нет единого решения, которое удовлетворяло бы большинству потребностей, которое стоит включить в библиотечную подпрограмму.(Если вам достаточно сравнения с относительным или абсолютным интервалом ошибок, вы можете сделать это просто без библиотечной процедуры.)