Сравните две переменные с плавающей точкой в ​​C ++ - PullRequest
1 голос
/ 08 мая 2019

Я прочитал несколько статей, касающихся сравнения переменных с плавающей запятой, но не смог понять и получить необходимые знания из этих статей. Итак, я задаю этот вопрос.

Каков хороший способ сравнения двух переменных с плавающей запятой? Ниже приведен фрагмент кода:

#define EPSILON_VALUE 0.0000000000000001

bool cmpf(float A, float B)
{
  return (fabs(A - B) < EPSILON_VALUE);
}

int main()
{
  float a = 1.012345679, b = 1.012345678;

  if(cmpf(a, b))cout<<"same"<<endl;
  else cout<<"different"<<endl;

  return 0;
}

Вывод: same хотя обе переменные с плавающей запятой имеют разные значения.

Ответы [ 3 ]

5 голосов
/ 08 мая 2019

Не существует общего решения для сравнения чисел с плавающей запятой, которые содержат ошибки предыдущих операций. Код, который должен использоваться, зависит от приложения. Поэтому, чтобы получить правильный ответ, вы должны описать свою ситуацию более конкретно.

Основная проблема заключается в том, что выполнение правильных вычислений с использованием неверных данных в общем случае невозможно. Если вы хотите вычислить некоторую функцию из двух точных математических значений x и y , но единственные данные, которые у вас есть, это некоторые неточно вычисленные значения x и y, это обычно невозможно вычислить точно правильный результат. Например, предположим, что вы хотите знать, какова сумма x + y , но вы знаете только, что x равно 3, а y равно 4, но вы не знаете знать, что такое истинные, точные x и y . Тогда вы не можете вычислить x + y .

Если вы знаете, что x и y равны приблизительно x и y , тогда вы можете вычислить приближение x + y, добавив x и y. Срабатывает, когда вычисляемая функция (+ в этом примере) имеет разумную производную: незначительное изменение входных данных функции с разумной производной слегка меняет ее выходные данные. Это терпит неудачу, когда функция, которую вы хотите вычислить, имеет разрыв или большую производную. Например, если вы хотите вычислить квадратный корень из x (в реальной области), используя приближение x, но x может быть отрицательным из-за предыдущих ошибок округления, тогда вычисление sqrt(x) может произвести исключение. Аналогично, сравнение неравенства или порядка является прерывистой функцией: незначительное изменение входных данных может полностью изменить ответ (с ложного на истинное или наоборот).

Обычный плохой совет - сравнивать с «толерантностью». Этот метод обменивает ложные отрицания (неправильные отклонения чисел, которые удовлетворяли бы сравнению, если сравнивались точные математические значения) для ложных положительных результатов (неправильное принятие чисел, которые не удовлетворяли бы сравнению).

Может ли применимое лицо терпеть ложное принятие, зависит от приложения. Следовательно, общего решения не существует.

Уровень допуска к установке и даже характер, по которому он рассчитывается, зависят от данных, ошибок и предыдущих вычислений. Таким образом, даже если допустимо сравнивать с допуском, количество допусков для использования и способ его расчета зависят от приложения. Там нет общего решения.

1 голос
/ 11 мая 2019

Вывод: один и тот же, хотя обе переменные с плавающей запятой содержат разные значения.

"переменные с плавающей запятой имеют разные значения".является необоснованным.

same было напечатано, потому что значения a,b одинаковы, даже если константы инициализации отличаются.


Типичный float является 32-разрядным и может представлять около 2 32 различных значений, таких как 1,0, 1024,0, 0,5, 0,125.Все эти значения имеют вид: +/- some_integer * 2 some_integer

1.012345679 и 1.012345678 равны , а не в этом наборе float. @ Rudy Velthuis .

1.012345 67165374755859375 // `float` member
1.012345 678
1.012345 679
1.012345 790863037109375   // `float` member

Аналогично применяется для double, но с большей точностью - обычно 64 бита.

1.012345679 и 1.012345678 являются не в этом double установлены либо

1.012345 67799999997106397131574340164661407470703125    // `double` member
1.012345 678
1.012345 6780000001931085762407747097313404083251953125  // `double` member
...
1.012345 6789999998317597373898024670779705047607421875  // `double` member
1.012345 679
1.012345 67900000005380434231483377516269683837890625    // `double` member

Это можно рассматривать как 2 шага округления.Код 1.012345679 округляется до ближайшего double 1.01234567900000005380434231483377516269683837890625.Затем задание округляет double до ближайшего float 1.01234567165374755859375.

float a = 1.012345679;
// 'a' has the value of 1.01234567165374755859375

Аналогично для b.Код 1.012345678 округляется до ближайшего double 1.01234567799999997106397131574340164661407470703125.Затем присвоение округляет double до ближайшего float 1.01234567165374755859375.

flaot b = 1.012345678;
// 'b' has the value of 1.01234567165374755859375

a и b имеют одинаковое значение.

0 голосов
/ 09 мая 2019

Это потому, что числа с плавающей точкой имеют точность 7 цифр.Если вы хотите большей точности, вам нужно использовать double или long double.

...