Powszechna mądrość, że liczb zmiennoprzecinkowych nie można porównywać dla równości, jest niedokładna. Liczby zmiennoprzecinkowe nie różnią się od liczb całkowitych: Jeśli ocenisz „a == b”, otrzymasz prawdę, jeśli są to liczby identyczne, a fałsz w przeciwnym razie (przy założeniu, że dwa NaN nie są oczywiście liczbami identycznymi).
Rzeczywisty problem jest następujący: jeśli wykonałem kilka obliczeń i nie jestem pewien, czy dwie liczby, które muszę porównać, są dokładnie poprawne, to co? Ten problem jest taki sam dla liczb zmiennoprzecinkowych, jak dla liczb całkowitych. Jeśli ocenisz wyrażenie całkowite „7/3 * 3”, nie będzie ono porównywane z „7 * 3/3”.
Załóżmy więc, że zapytaliśmy „Jak porównać liczby całkowite w celu zapewnienia równości?” w takiej sytuacji. Nie ma jednej odpowiedzi; to, co powinieneś zrobić, zależy od konkretnej sytuacji, w szczególności od rodzaju błędów i tego, co chcesz osiągnąć.
Oto kilka możliwych opcji.
Jeśli chcesz uzyskać „prawdziwy” wynik, jeśli matematycznie dokładne liczby byłyby równe, możesz spróbować użyć właściwości obliczeń, które wykonujesz, aby udowodnić, że otrzymujesz te same błędy w tych dwóch liczbach. Jeśli jest to wykonalne, a porównasz dwie liczby, które wynikają z wyrażeń, które dałyby równe liczby, jeśli zostałyby dokładnie obliczone, uzyskasz „prawdziwość” z porównania. Inne podejście polega na tym, że możesz przeanalizować właściwości obliczeń i udowodnić, że błąd nigdy nie przekracza określonej kwoty, być może kwoty bezwzględnej lub kwoty odnoszącej się do jednego z danych wejściowych lub jednego z danych wyjściowych. W takim przypadku możesz zapytać, czy dwie obliczone liczby różnią się co najwyżej o tę kwotę, i zwrócić „prawda”, jeśli mieszczą się w przedziale. Jeśli nie możesz udowodnić błędu, możesz zgadywać i mieć nadzieję na najlepsze. Jednym ze sposobów zgadywania jest ocena wielu losowych próbek i sprawdzenie, jaki rozkład otrzymujesz w wynikach.
Oczywiście, ponieważ ustaliliśmy wymóg, abyś był „prawdziwy”, jeśli matematycznie dokładne wyniki są równe, pozostawiliśmy otwartą możliwość, abyś był „prawdziwy”, nawet jeśli są nierówne. (W rzeczywistości możemy spełnić ten wymóg, zawsze zwracając „prawda”. To sprawia, że obliczenia są proste, ale generalnie niepożądane, dlatego omówię poprawę sytuacji poniżej.)
Jeśli chcesz uzyskać „fałszywy” wynik, jeśli matematyczne dokładne liczby byłyby nierówne, musisz udowodnić, że twoja ocena liczb daje różne liczby, jeśli matematyczne dokładne liczby byłyby nierówne. Może to być niemożliwe ze względów praktycznych w wielu typowych sytuacjach. Rozważmy więc alternatywę.
Przydatnym wymogiem może być uzyskanie „fałszywego” wyniku, jeśli matematyczne dokładne liczby różnią się o więcej niż pewną liczbę. Na przykład być może obliczymy, gdzie wędrowała piłka rzucona w grę komputerową, i chcemy wiedzieć, czy uderzyła w nietoperza. W tym przypadku z pewnością chcemy uzyskać „prawdziwą”, jeśli piłka uderzy w nietoperza, a także „fałszywą”, jeśli piłka jest daleko od nietoperza, i możemy zaakceptować niepoprawną „prawdziwą” odpowiedź, jeśli piłka matematycznie dokładna symulacja nie trafiła nietoperza, ale znajduje się w odległości milimetra od uderzenia nietoperza. W takim przypadku musimy udowodnić (lub zgadnąć / oszacować), że nasze obliczenia pozycji piłki i pozycji nietoperza mają łączny błąd co najwyżej jednego milimetra (dla wszystkich pozycji zainteresowania). To pozwoli nam zawsze wracać ”
To, jak zdecydujesz, co zwrócić, porównując liczby zmiennoprzecinkowe, zależy w dużej mierze od konkretnej sytuacji.
Jeśli chodzi o sprawdzanie granic błędów w obliczeniach, może to być skomplikowany temat. Każda implementacja zmiennoprzecinkowa wykorzystująca standard IEEE 754 w trybie zaokrąglania do najbliższego zwraca liczbę zmiennoprzecinkową najbliższą dokładnemu wynikowi dla dowolnej podstawowej operacji (w szczególności mnożenie, dzielenie, dodawanie, odejmowanie, pierwiastek kwadratowy). (W przypadku remisu, zaokrąglenia, tak więc niski bit jest parzysty.) (Zachowaj szczególną ostrożność przy pierwiastku kwadratowym i dzieleniu; twoja implementacja języka może używać metod, które nie są zgodne z IEEE 754). Z tego powodu wiemy, że błąd w pojedynczym wyniku wynosi najwyżej 1/2 wartości najmniej znaczącego bitu. (Gdyby było więcej, zaokrąglenie byłoby ustawione na inną liczbę, która mieści się w zakresie 1/2 wartości.)
Dalsza praca staje się znacznie bardziej skomplikowana; następnym krokiem jest wykonanie operacji, w której jedno z wejść ma już jakiś błąd. W przypadku prostych wyrażeń błędy te można śledzić przez obliczenia, aby osiągnąć granicę błędu końcowego. W praktyce odbywa się to tylko w kilku sytuacjach, takich jak praca nad wysokiej jakości biblioteką matematyczną. I oczywiście potrzebujesz dokładnej kontroli dokładnie nad tym, które operacje są wykonywane. Języki wysokiego poziomu często dają kompilatorowi dużo luzu, więc możesz nie wiedzieć, w jakiej kolejności są wykonywane operacje.
Jest o wiele więcej, co można napisać (i jest) na ten temat, ale muszę się na tym zatrzymać. Podsumowując, odpowiedź brzmi: nie ma procedury bibliotecznej dla tego porównania, ponieważ nie ma jednego rozwiązania, które byłoby w stanie zaspokoić większość potrzeb, które byłoby warte zastosowania w bibliotece. (Jeśli porównanie z przedziałem błędu względnego lub bezwzględnego wystarcza, możesz to zrobić po prostu bez procedury bibliotecznej).