Плавающая точка, насколько я могу доверять меньше или больше сравнений?
Скажем, у меня есть два числа с плавающей запятой, и я хочу их сравнить. Если один больше другого, программа должна взять одну вилку. Если противоположное верно, оно должно пройти другим путем. И он должен делать то же самое, если сравниваемое значение слегка подтолкнет в направлении, которое все равно должно заставить его сравнить true.
Это сложный вопрос для фразы, поэтому я написал это, чтобы продемонстрировать это -
float a = random();
float b = random(); // always returns a number (no infinity or NaNs)
if(a < b){
if( !(a < b + FLOAT_EPISILON) ) launchTheMissiles();
buildHospitals();
}else if(a >= b){
if( !(a >= b - FLOAT_EPISILON) ) launchTheMissiles();
buildOrphanages();
}else{
launchTheMissiles(); // This should never be called, in any branch
}
С учетом этого кода гарантируется, что launchTheMissiles()
никогда не будет вызываться?
Ответы
Ответ 1
Если вы можете гарантировать, что a
и b
не являются NaN или бесконечностями, вы можете просто сделать:
if (a<b) {
…
} else {
…
}
Множество всех значений с плавающей запятой, за исключением бесконечностей и NaN, содержит полное упорядочение (с глюком с двумя представлениями нуля, но это не имеет для вас значения), что мало чем отличается от работы с нормальным набором целых чисел - единственное различие заключается в том, что величина интервалов между последующими значениями не является постоянной, как и целыми числами.
Фактически, IEEE 754 был спроектирован таким образом, что сравнение значений non-infinity, отличных от NaN, одного и того же знака может быть выполнено с теми же операциями, что и нормальные целые числа (опять же, с ошибкой с нулем). Итак, в этом конкретном случае вы можете думать об этих числах как о "лучших целых числах".
Ответ 2
Короткий ответ, он никогда не будет вызван.
Если a<b
, то a всегда будет меньше b плюс положительная сумма, какая бы она ни была. В этом случае проверка, если a меньше, чем b +, будет действительной.
Третий случай не будет достигнут.
Ответ 3
Стандарт IEEE 754 (с плавающей запятой) утверждает, что сложение или вычитание могут привести к положительной или отрицательной бесконечности, поэтому b + FLOAT_EPSILON и b - FLOAT_EPSILON могут привести к положительной или отрицательной бесконечности, если b - FLT_MAX или -FLT_MAX. В стандарте с плавающей запятой также указано, что бесконечность сравнивается, как и следовало ожидать, с FLT_MAX < + бесконечность, возвращающая true, и -FLT_MAX > -infinity.
Для более пристального рассмотрения формата с плавающей запятой и точности вопросов с практической точки зрения, я рекомендую взглянуть на книгу Криса Эриксона "Обнаружение столкновений в реальном времени" или в сообщениях Брюса Доусона по этой теме, последняя из которых (с приятной оглавление!) находится на http://randomascii.wordpress.com/2013/02/07/float-precision-revisited-nine-digit-float-portability/.
Ответ 4
Тесты на неравенство точны, как и тесты для равенства. Люди путаются, потому что они не понимают, что ценности, с которыми они работают, могут быть не такими, какими они себя считают. Итак, да, комментарий к окончательному вызову функции правильный. Эта ветка никогда не будет принята.
Ответ 5
Как насчет меньше, чем проверка с помощью окна epsilon? если a меньше b, то a не может быть равно b
/**
* checks whether a <= b with epsilon window
*/
template <typename T>
bool eq(T a, T b){
T e = std::numeric_limits<T>::epsilon();
return std::fabs(a-b) <= e;
}
/**
* checks whether a < b with epsilon window
*/
template <typename T>
bool lt(T a, T b){
if(!eq(a,b)){ // if a < b then a != b
return a < b;
}
return false;
}
/**
* checks whether a <= b with epsilon window
*/
template <typename T>
bool lte(T a, T b){
if(eq(a,b)){
return true;
}
return a < b;
}