Porównując IEEE floats i doubles dla równości

Jaka jest najlepsza metoda porównywania IEEE floats i doubles dla równości? Słyszałem o kilku metodach, ale chciałem zobaczyć, co myśli społeczność.

Author: Craig H, 2008-08-22

15 answers

Moim zdaniem najlepszym podejściem jest porównanie ULPs .

bool is_nan(float f)
{
    return (*reinterpret_cast<unsigned __int32*>(&f) & 0x7f800000) == 0x7f800000 && (*reinterpret_cast<unsigned __int32*>(&f) & 0x007fffff) != 0;
}

bool is_finite(float f)
{
    return (*reinterpret_cast<unsigned __int32*>(&f) & 0x7f800000) != 0x7f800000;
}

// if this symbol is defined, NaNs are never equal to anything (as is normal in IEEE floating point)
// if this symbol is not defined, NaNs are hugely different from regular numbers, but might be equal to each other
#define UNEQUAL_NANS 1
// if this symbol is defined, infinites are never equal to finite numbers (as they're unimaginably greater)
// if this symbol is not defined, infinities are 1 ULP away from +/- FLT_MAX
#define INFINITE_INFINITIES 1

// test whether two IEEE floats are within a specified number of representable values of each other
// This depends on the fact that IEEE floats are properly ordered when treated as signed magnitude integers
bool equal_float(float lhs, float rhs, unsigned __int32 max_ulp_difference)
{
#ifdef UNEQUAL_NANS
    if(is_nan(lhs) || is_nan(rhs))
    {
        return false;
    }
#endif
#ifdef INFINITE_INFINITIES
    if((is_finite(lhs) && !is_finite(rhs)) || (!is_finite(lhs) && is_finite(rhs)))
    {
        return false;
    }
#endif
    signed __int32 left(*reinterpret_cast<signed __int32*>(&lhs));
    // transform signed magnitude ints into 2s complement signed ints
    if(left < 0)
    {
        left = 0x80000000 - left;
    }
    signed __int32 right(*reinterpret_cast<signed __int32*>(&rhs));
    // transform signed magnitude ints into 2s complement signed ints
    if(right < 0)
    {
        right = 0x80000000 - right;
    }
    if(static_cast<unsigned __int32>(std::abs(left - right)) <= max_ulp_difference)
    {
        return true;
    }
    return false;
}
Podobną technikę można zastosować dla sobowtórów. Sztuczka polega na przekonwertowaniu pływaków tak, aby były uporządkowane (jak liczby całkowite), a następnie po prostu zobaczyć, jak różne są. Nie mam pojęcia, dlaczego to cholerstwo psuje moje podkreślenia. Edit: o, Może to tylko artefakt podglądu. W porządku.
 7
Author: DrPizza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 22:09:57

Aktualna wersja, której używam to

bool is_equals(float A, float B,
               float maxRelativeError, float maxAbsoluteError)
{

  if (fabs(A - B) < maxAbsoluteError)
    return true;

  float relativeError;
  if (fabs(B) > fabs(A))
    relativeError = fabs((A - B) / B);
  else
    relativeError = fabs((A - B) / A);

  if (relativeError <= maxRelativeError)
    return true;

  return false;
}

To rozwiązuje większość problemów, łącząc względną i absolutną tolerancję błędów. Czy podejście ULP jest lepsze? Jeśli tak, to dlaczego?

 3
Author: Craig H,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 22:22:26

@DrPizza: nie jestem guru wydajności, ale spodziewałbym się, że operacje ze stałymi punktami będą szybsze niż operacje zmiennoprzecinkowe (w większości przypadków).

To raczej zależy od tego, co z nimi robisz. Typ stałopunktowy o tym samym zakresie co zmiennoprzecinkowy IEEE byłby wielokrotnie wolniejszy (i wielokrotnie większy).

Rzeczy odpowiednie dla pływaków:

Grafika 3D, fizyka / inżynieria, symulacja, symulacja klimatu....

 1
Author: DrPizza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 22:57:00

W oprogramowaniu numerycznym często chcemy sprawdzić, czy dwie liczby zmiennoprzecinkowe są dokładnie równe. LAPACK jest pełen przykładów takich przypadków. Oczywiście najczęstszym przypadkiem jest sprawdzenie, czy Liczba zmiennoprzecinkowa jest równa "Zero", "jeden", "dwa", "połowa". Jeśli ktoś jest zainteresowany mogę wybrać kilka algorytmów i przejść bardziej szczegółowo.

Również w BLAS często chcesz sprawdzić, czy Liczba zmiennoprzecinkowa jest dokładnie zerem czy jedynką. Na przykład, rutynowe dgemv można obliczyć operacje formularza

  • y = beta * y + alfa * a * x
  • y = beta * y + Alfa*A^T * x
  • y = beta * y + Alfa * A^H * x

Więc jeśli beta równa się 1, masz "przypisanie plus", a dla beta równa się Zero "przypisanie proste". Więc na pewno można obniżyć koszty obliczeniowe, jeśli dać te (wspólne) przypadki specjalnego traktowania.

Oczywiście, możesz zaprojektować procedury BLAS w taki sposób, aby uniknąć dokładnych porównań (np. używając niektórych FLAG). Jednak LAPACK jest pełen przykładów, w których nie jest to możliwe.

P. S.:

  • Z pewnością istnieje wiele przypadków, w których nie chcesz sprawdzić "jest dokładnie równe". Dla wielu ludzi może to być nawet jedyny przypadek, z którym mają do czynienia. Chcę tylko podkreślić, że są też inne sprawy.

  • Chociaż LAPACK jest napisany w Fortranie, logika jest taka sama, jeśli używasz innych języków programowania do oprogramowania numerycznego.

 1
Author: Michael Lehn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-11 07:33:11

Dobry Boże, proszę nie interpretować bitów float jako ints, chyba że używasz P6 lub wcześniej.

Nawet jeśli powoduje kopiowanie z rejestrów wektorowych do rejestrów całkowitych za pośrednictwem pamięci, a nawet jeśli blokuje rurociąg, jest to najlepszy sposób, na jaki się natknąłem, ponieważ zapewnia najbardziej solidne porównania nawet w obliczu błędów zmiennoprzecinkowych.

To jest cena, którą warto zapłacić.
 0
Author: DrPizza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 21:59:00

To rozwiązuje większość problemów, łącząc względną i absolutną tolerancję błędów. Czy podejście ULP jest lepsze? Jeśli tak, to dlaczego?

ULP są bezpośrednią miarą "odległości" między dwoma liczbami zmiennoprzecinkowymi. Oznacza to, że nie wymagają od Ciebie wyczarowania względnych i bezwzględnych wartości błędów, ani nie musisz się upewnić, że uzyskasz te wartości "w porządku". Dzięki ULPs możesz bezpośrednio wyrazić, jak blisko chcesz, aby liczby były, i to samo próg działa równie dobrze dla małych wartości, jak dla dużych.

 0
Author: DrPizza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 22:07:11

Jeśli masz błędy zmiennoprzecinkowe, masz jeszcze więcej problemów niż to. Chociaż myślę, że to zależy od osobistej perspektywy.

Nawet jeśli wykonamy analizę numeryczną, aby zminimalizować nagromadzenie błędów, nie możemy go wyeliminować i możemy pozostawić wyniki, które powinny być identyczne (jeśli obliczamy za pomocą liczb rzeczywistych), ale różnią się (ponieważ nie możemy obliczyć za pomocą liczb rzeczywistych).

 0
Author: DrPizza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 22:09:05

Jeśli szukasz dwóch pływaków, aby były równe, to moim zdaniem powinny być identyczne. Jeśli masz do czynienia z problemem zaokrąglania zmiennoprzecinkowego, być może reprezentacja punktu stałego będzie lepiej odpowiadać twojemu problemowi.

 0
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 22:09:43

Jeśli szukasz dwóch pływaków, aby były równe, to moim zdaniem powinny być identyczne. Jeśli masz do czynienia z problemem zaokrąglania zmiennoprzecinkowego, być może reprezentacja punktu stałego będzie lepiej odpowiadać twojemu problemowi.

Być może nie możemy sobie pozwolić na utratę zasięgu lub wydajności, które takie podejście spowodowałoby.

 0
Author: DrPizza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 22:11:27

@ DrPizza: nie jestem guru wydajności, ale spodziewałbym się, że operacje ze stałymi punktami będą szybsze niż operacje zmiennoprzecinkowe (w większości przypadków).

@Craig H: Jasne. Nie mam nic przeciwko temu, żeby to wydrukować. Jeśli a lub b przechowują pieniądze, powinny być reprezentowane w punkcie stałym. Ciężko mi wymyślić przykład z prawdziwego świata, gdzie taka logika powinna być sprzymierzona z pływakami. Rzeczy odpowiednie dla pływaków:

  • wagi
  • szeregi
  • odległości
  • real world wartości (jak z ADC)

Dla wszystkich tych rzeczy, albo dużo wtedy Liczby i po prostu przedstawić wyniki dla użytkownika do ludzkiej interpretacji, lub złożyć oświadczenie porównawcze (nawet jeśli takie stwierdzenie jest, "ta rzecz jest w 0.001 tej drugiej rzeczy"). Twierdzenie porównawcze takie jak moje jest przydatne tylko w kontekście algorytmu: część "w ciągu 0,001" zależy od tego, jakie pytanie fizyczne zadajesz. To mój 0.02. A może 2/100?

 0
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 22:51:20

To raczej zależy od tego, kim jesteś robiąc z nimi. Typ stałopunktowy z tym samym zakresem co zmiennoprzecinkowy IEEE byłoby wiele razy wolniej (i wielokrotnie większe).

Dobra, ale jeśli chcę nieskończenie małej rozdzielczości bitowej, to wracam do mojego pierwotnego punktu: = = i != nie mają znaczenia w kontekście takiego problemu.

Int pozwala mi wyrazić ~10^9 wartości (niezależnie od zakresu), który wydaje się wystarczający dla każdej sytuacji, w której zależy mi o dwóch równych sobie. A jeśli to nie wystarczy, użyj 64-bitowego systemu operacyjnego i masz około 10^19 różnych wartości.

Mogę wyrazić wartości z zakresu od 0 do 10^200 (na przykład) w int, to tylko rozdzielczość bitowa, która cierpi (rozdzielczość byłaby większa niż 1, ale, znowu, żadna aplikacja nie ma tego rodzaju zakresu, jak również tego rodzaju rozdzielczości).

Podsumowując, myślę, że we wszystkich przypadkach jeden albo reprezentuje kontinuum wartości, w takim przypadku != i = = są nieistotne, lub jeden reprezentuje stały zestaw wartości, które mogą być odwzorowane do int (lub innego typu O STAŁEJ precyzji).

 0
Author: Nick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 23:27:11

Int pozwala mi wyrazić ~10^9 wartości (niezależnie od zakresu), który wydaje się jak wystarczająco w każdej sytuacji, w której zależy by dwóch z nich było równe. A jeśli to nie wystarczy, użyj 64-bitowy OS i masz około 10^19 różne wartości.

Osiągnąłem ten limit... Próbowałem żonglować czasem w PS i czasem w cyklach zegarowych w symulacji, w której łatwo trafisz 10^10 cykli. Bez względu na to, co zrobiłem, bardzo szybko przepełniłem mizerny zakres 64-bitowe liczby całkowite... 10^19 to nie tyle co myślisz, daj mi teraz 128 bitów!

Pływaki pozwoliły mi znaleźć rozwiązanie problemów matematycznych, ponieważ wartości przepełnione są zerami w dolnej części. Więc w zasadzie miał kropkę dziesiętną pływające aronud w liczbie bez utraty precyzji (mógłbym jak z bardziej ograniczoną odrębną liczbę wartości dozwolone w mantissa float w porównaniu do 64-bitowy int, ale rozpaczliwie potrzebne zakres TH!).

I wtedy rzeczy zamienione z powrotem na liczby całkowite do porównania itp.

Irytujące, a w końcu zrezygnowałem z całej próby i po prostu polegałem na pływakach i , aby wykonać pracę. Nie jest idealny, ale działa na przewidywany przypadek użycia.
 0
Author: jakobengblom2,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-10-09 14:07:07

Jeśli szukasz dwóch pływaków, aby były równe, to moim zdaniem powinny być identyczne. Jeśli masz do czynienia z problemem zaokrąglania zmiennoprzecinkowego, być może reprezentacja punktu stałego będzie lepiej odpowiadać twojemu problemowi.

Może powinienem lepiej wyjaśnić problem. W C++ następujący kod:
#include <iostream>

using namespace std;


int main()
{
  float a = 1.0;
  float b = 0.0;

  for(int i=0;i<10;++i)
  {
    b+=0.1;
  }

  if(a != b)
  {
    cout << "Something is wrong" << endl;
  }

  return 1;
}

Wypisuje frazę "coś jest nie tak". Chcesz powiedzieć, że powinno?

 0
Author: Craig H,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-10 04:07:58

Dobry Boże, proszę nie interpretować bitów float jako ints, chyba że używasz P6 lub wcześniej.

 0
Author: Mat Noguchi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-03 14:46:43

Jest to najlepszy sposób, na jaki się natknąłem, ponieważ zapewnia najbardziej solidne porównania nawet w obliczu błędów zmiennoprzecinkowych.

Jeśli masz błędy zmiennoprzecinkowe, masz jeszcze więcej problemów niż to. Chociaż myślę, że to zależy od osobistej perspektywy.

 0
Author: Mat Noguchi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-03 15:00:35