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