Po pierwsze, kilka rzeczy do uwaga:
- „standardowych” sposób to zrobić, to wybrać stałą epsilon, ale stałe Epsilonów nie działają poprawnie na wszystkie zakresy numer.
- Jeśli chcesz użyć stałego epsilon
sqrt(EPSILON)
, pierwiastek kwadratowy epsilon z float.h
jest ogólnie uważany za dobrą wartość. (pochodzi z niesławnej "pomarańczowej książki", której nazwisko ucieka w tej chwili).
- Podział na zmiennoprzecinkowe będzie powolny, więc prawdopodobnie będziecie go unikać do porównań, nawet jeśli zachowuje się jak wybieranie epsilonu wykonanego na zamówienie dla wielkości liczb.
Co naprawdę chcesz zrobić? coś takiego:
Porównaj, ile reprezentowalnych liczb zmiennoprzecinkowych różnią się wartości.
Ten kod pochodzi z this naprawdę świetny artykuł Bruce Dawson. Artykuł został zaktualizowany here. Główna różnica polega na tym, że stary artykuł łamie zasadę ścisłego aliasingu. (rzutowanie zmiennoprzecinkowych wskaźników na wskaźnik, dereferencje, pisanie, odlewanie). Podczas gdy purystyczny C/C++ szybko wskaże wadę, w praktyce to działa i uważam, że kod jest bardziej czytelny. Jednak nowy artykuł używa związków, a C/C++ zachowuje swoją godność. Dla zwięzłości podaję kod, który łamie ścisłe aliasing poniżej.
// Usable AlmostEqual function
bool AlmostEqual2sComplement(float A, float B, int maxUlps)
{
// Make sure maxUlps is non-negative and small enough that the
// default NAN won't compare as equal to anything.
assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024);
int aInt = *(int*)&A;
// Make aInt lexicographically ordered as a twos-complement int
if (aInt < 0)
aInt = 0x80000000 - aInt;
// Make bInt lexicographically ordered as a twos-complement int
int bInt = *(int*)&B;
if (bInt < 0)
bInt = 0x80000000 - bInt;
int intDiff = abs(aInt - bInt);
if (intDiff <= maxUlps)
return true;
return false;
}
Podstawową ideą w powyższym kodzie jest pierwszym ogłoszeniu, że dany IEEE 754 formacie zmiennoprzecinkowym, {sign-bit, biased-exponent, mantissa}
, że numery są uporządkowane leksykograficznie jeśli interpretować jako podpisanych wskazówki wielkości. To znaczy, że bit znaku staje się bitem znaku, a wykładnik zawsze całkowicie przewyższa mantysę w definiowaniu wielkości pływaka, a ponieważ jest on pierwszy w określaniu wielkości liczby interpretowanej jako int.
Tak więc, interpretujemy bitową reprezentację liczby zmiennoprzecinkowej jako int o sygnaturze mag. Następnie konwertujemy ints o sygnowanej magii na dwuskładnikowe ints, odejmując je od 0x80000000, jeśli liczba jest ujemna. Następnie porównujemy te dwie wartości, tak jak każdą z dwóch uzupełniających się sygnatur z dwoma podpisami i sprawdzając, ile wartości różnią się one. Jeśli ta kwota jest mniejsza od progu, który wybierzesz w odniesieniu do liczby reprezentowalnych wartości zmiennoprzecinkowych, wartości mogą się różnić i nadal być traktowane jako równe, wtedy mówisz, że są one "równe". Zauważ, że ta metoda poprawnie pozwala, aby "równe" liczby różniły się większymi wartościami dla pływających o większej wielkości, oraz o mniejszymi wartościami dla mniejszych wartości pływowych.
Na marginesie, jeśli używasz 'float' i obejmuje on pieniądze (lub naprawdę dowolną liczbę zwykle przedstawianą w postaci liczb dziesiętnych), powinieneś rozważyć opcję' BigDecimal'. –
dobry komentarz. Jestem tego świadomy i używam BigDecimal do wszystkich monetarnych rzeczy. Ale dotyczy to wszystkiego innego. –
Użycie java.lang.Float.compare (float1, float2); // zwraca wartość całkowitą, ale nie można jej użyć zamiast float1 == float2 w warunku if –