Сравните два поплавка - PullRequest
9 голосов
/ 13 мая 2011
#include <stdbool.h>

bool Equality(double a, double b, double epsilon)
{
  if (fabs(a-b) < epsilon) return true;
  return false;
}

Я попробовал этот метод для сравнения двух двойных чисел, но у меня всегда возникают проблемы, так как я не знаю, как выбрать epsilon, на самом деле я хочу сравнить маленькие числа (6 6 цифр после десятичной точки)как 0,000001 .Я пробовал с некоторыми числами, иногда я получаю 0.000001 != 0.000001, а иногда 0.000001 == 0.000002 Есть ли другой способ, кроме сравнения с эпсилоном?

Моя цель - сравнить два двойных числа (которые представляют время в моем случае),Переменная t, которая представляет время в миллисекундах, является двойной.Он увеличивается на другую функцию 0,000001, затем на 0,000002 и т. Д. Каждый раз, когда изменяется t, я хочу проверить, равна ли она другой переменной типа double tt, в случае tt == t у меня есть некоторые инструкции для выполнения ..
Спасибо за вашу помощь

Ответы [ 5 ]

16 голосов
/ 13 мая 2011

Смотрите здесь: http://floating -point-gui.de / errors / Сравнение /

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

И, конечно, Что должен знать каждый ученый об арифметике с плавающей точкой

3 голосов
/ 13 мая 2011

Во-первых: нет смысла вычислять логическое значение (с помощью оператора <), а затем помещать его в другое логическое значение. Просто напишите это так:

bool Equality(float a, float b, float epsilon)
{
  return fabs(a - b) < epsilon;
}

Во-вторых, вполне возможно, что ваш эпсилон не очень хорошо представлен как float и, следовательно, не выглядит так, как вы ожидаете. Попробуйте с отрицательной степенью 2, например, 1/1048576.

1 голос
/ 08 июня 2017

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

BOOL floatcmp(float float1, float float2, unsigned int precision){
   int int1, int2;

   if (float1 > 0)
      int1 = (int)(float1 * precision + .5);
   else
      int1 = (int)(float1 * precision - .5);

   if (float2 > 0)
      int2 = (int)(float2 * precision + .5);
   else
      int2 = (int)(float2 * precision - .5);

   return (int1 == int2);
}
0 голосов
/ 27 ноября 2017

Я пишу и тестирую этот код. Кажется, работает.

public static boolean equal(double a, double b) {
    final long fm = 0xFFFFFFFFFFFFFL;       // fraction mask
    final long sm = 0x8000000000000000L;    // sign mask
    final long cm = 0x8000000000000L;       // most significant decimal bit mask
    long c = Double.doubleToLongBits(a), d = Double.doubleToLongBits(b);        
    int ea = (int) (c >> 52 & 2047), eb = (int) (d >> 52 & 2047);
    if (ea == 2047 && (c & fm) != 0 || eb == 2047 && (d & fm) != 0) return false;   // NaN 
    if (c == d) return true;                            // identical - fast check
    if (ea == 0 && eb == 0) return true;                // ±0 or subnormals
    if ((c & sm) != (d & sm)) return false;             // different signs
    if (abs(ea - eb) > 1) return false;                 // b > 2*a or a > 2*b
    d <<= 12; c <<= 12;
    if (ea < eb) c = c >> 1 | sm;
    else if (ea > eb) d = d >> 1 | sm;
    c -= d;
    return c < 65536 && c > -65536;     // don't use abs(), because:
    // There is a posibility c=0x8000000000000000 which cannot be converted to positive
}
public static boolean zero(double a) { return (Double.doubleToLongBits(a) >> 52 & 2047) < 3; }
  • если любое из чисел равно NaN, числа не равны.
  • если 2 числа идентичны, равны. Это быстрая начальная проверка.
  • если оба числа являются +0, -0 или субнормальными, числа равны.
  • если числа имеют разные знаки, числа не равны. Это выглядит как неправильный подход, если оба числа почти 0 (но не ± 0 или субнормалы) с разными знаками. Но что если вы умножите эти числа на другое? один результат будет отрицательным, а другой положительным. Итак, мы строги, и это правильно.
  • если показатели имеют разность 2 или более, числа не равны, поскольку одно число как минимум в 2 раза больше другого.
  • если показатели имеют разность ровно 1, правильно сдвиньте дробь из одного из чисел.
  • Если разница в 2 дроби мала, числа равны.
0 голосов
/ 27 ноября 2017

Имейте в виду, что когда float a = +2^(254-127) * 1.___22 zeros___1 и float b = +2^(254-127) * 1.___23 zeros___, то мы ожидаем abs(a-b) < epsilon, но вместо a - b = +2^(254-127-23) * 1.___23 zeros___ = 20282409603651670423947251286000, что намного больше, чем эпсилон ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...