Poniższy kod jest oczywiście nieprawidłowy. Jaki jest problem?
i <- 0.1
i <- i + 0.05
i
## [1] 0.15
if(i==0.15) cat("i equals 0.15") else cat("i does not equal 0.15")
## i does not equal 0.15
r
floating-point
floating-accuracy
r-faq
dplanet
źródło
źródło
Odpowiedzi:
Powód ogólny (agnostyczny z języka)
Ponieważ nie wszystkie liczby można przedstawić dokładnie w arytmetyce zmiennoprzecinkowej IEEE (standard, którego prawie wszystkie komputery używają do reprezentowania liczb dziesiętnych i wykonywania z nimi matematyki), nie zawsze otrzymasz to, czego się spodziewałeś. Jest to szczególnie prawdziwe, ponieważ niektóre wartości, które są prostymi, skończonymi liczbami dziesiętnymi (np. 0,1 i 0,05), nie są dokładnie reprezentowane w komputerze, więc wyniki arytmetyki na nich mogą nie dać wyniku identycznego z bezpośrednią reprezentacją „ znana „odpowiedź.
Jest to dobrze znane ograniczenie arytmetyki komputerowej i jest omawiane w kilku miejscach:
Porównywanie skalarów
Standardowym rozwiązaniem tego
R
nie jest użycie==
, aleall.equal
funkcja. Albo raczej, ponieważall.equal
daje wiele szczegółów na temat różnic, jeżeli istnieją,isTRUE(all.equal(...))
.daje
Więcej przykładów użycia
all.equal
zamiast==
(ostatni przykład ma wykazać, że poprawnie pokażą różnice).Więcej szczegółów, bezpośrednio skopiowanych z odpowiedzi na podobne pytanie :
Problem, z którym się spotkałeś, polega na tym, że zmiennoprzecinkowe nie mogą reprezentować ułamków dziesiętnych dokładnie w większości przypadków, co oznacza, że często zdarza się, że dokładne dopasowanie nie powiedzie się.
podczas gdy R leży lekko, gdy mówisz:
Możesz dowiedzieć się, co tak naprawdę myśli dziesiętnie:
Widać, że te liczby są różne, ale reprezentacja jest nieco nieporęczna. Jeśli spojrzymy na nie w systemie binarnym (cóż, heks, co jest równoważne), otrzymamy wyraźniejszy obraz:
Widać, że różnią się one
2^-53
, co jest ważne, ponieważ liczba ta jest najmniejszą możliwą do reprezentacji różnicą między dwiema liczbami, których wartość jest bliska 1, jak to jest.Możemy dowiedzieć się dla każdego komputera, jaki jest ten najmniejszy reprezentowalny numer, patrząc na pole maszyny R.
Możesz użyć tego faktu, aby utworzyć funkcję „prawie równą”, która sprawdza, czy różnica jest bliska najmniejszej reprezentowalnej liczbie w liczbach zmiennoprzecinkowych. W rzeczywistości to już istnieje:
all.equal
.Zatem funkcja all.equal faktycznie sprawdza, czy różnica między liczbami jest pierwiastkiem kwadratowym najmniejszej różnicy między dwiema mantysami.
Ten algorytm jest nieco zabawny w pobliżu bardzo małych liczb zwanych denormalami, ale nie musisz się tym martwić.
Porównywanie wektorów
Powyższa dyskusja zakładała porównanie dwóch pojedynczych wartości. W języku R nie ma skalarów, tylko wektory, a ukryta wektoryzacja to siła języka. Przy porównywaniu wartości wektorów elementarnych obowiązują poprzednie zasady, ale implementacja jest nieco inna.
==
jest wektoryzowany (dokonuje porównania elementarnego), podczas gdyall.equal
porównuje całe wektory jako jedną całość.Korzystając z poprzednich przykładów
==
nie daje „oczekiwanego” wyniku iall.equal
nie wykonuje elementuZamiast tego należy użyć wersji, która zapętla dwa wektory
Jeśli pożądana jest jej funkcjonalna wersja, można ją zapisać
które można nazwać sprawiedliwym
Alternatywnie, zamiast owijać
all.equal
jeszcze więcej wywołań funkcji, możesz po prostu powielić odpowiednie elementy wewnętrzneall.equal.numeric
i użyć niejawnej wektoryzacji:Takie jest podejście
dplyr::near
, które dokumentuje się jakoźródło
Dodając do komentarza Briana (z tego powodu) możesz to zrobić, używając
all.equal
zamiast tego:Ostrzeżenie Jozuego tutaj to zaktualizowany kod (Dzięki Joshua):
źródło
all.equal
nie zwraca,FALSE
gdy występują różnice, więc musisz go owinąć,isTRUE
gdy używasz go wif
instrukcji.To jest hackerskie, ale szybkie:
źródło
all.equal(... tolerance)
parametru.all.equal(0.147, 0.15, tolerance=0.05)
jest prawdziwy.dplyr::near()
jest opcją do testowania, czy dwa wektory liczb zmiennoprzecinkowych są równe. Oto przykład z dokumentacji :Funkcja ma wbudowany parametr tolerancji:
tol = .Machine$double.eps^0.5
można go regulować. Domyślny parametr jest taki sam jak domyślny dlaall.equal()
.źródło
Miałem podobny problem. Użyłem następującego rozwiązania.
generowanie nierównych przedziałów cięcia na podstawie opcji (cyfry = 2):
wyjście równych przedziałów cięcia na podstawie funkcji okrągłej:
źródło
Uogólnione porównania („<=”, „> =”, „=”) w podwójnej arytmetyki:
Porównywanie <= b:
Porównywanie a> = b:
Porównywanie a = b:
źródło