2009-09-20 8 views
6

Dlaczego ten kod 7.30 - 7.20 w rubinach zwraca 0.0999999999999996, a nie 0.10?Arytmetyka w rubinach

Ale jeśli napiszę na przykład 7.30 - 7.16, wszystko będzie dobrze, otrzymam 0.14.

Na czym polega problem i jak mogę go rozwiązać?

Odpowiedz

1

Jest to typowy błąd związany z tym, jak liczby zmiennoprzecinkowe są reprezentowane w pamięci.

Użyj BigDecimal, jeśli potrzebujesz dokładnych wyników.

result=BigDecimal.new("7.3")-BigDecimal("7.2") 
puts "%2.2f" % result 
+0

Dyskusja na ten temat jest bardzo dobra: http://whynotwiki.com/Ruby_/_Numeries –

+2

Pamiętaj, że tylko BigDecimal daje dokładne wyniki, jeśli liczba ma skończoną liczbę cyfr w baza 10. – sepp2k

+0

http://floating-point-gui.de/ –

3

Problem polega na tym, że floating point is inaccurate. Możesz go rozwiązać za pomocą Rational, BigDecimal lub po prostu liczb całkowitych (na przykład, jeśli chcesz przechowywać pieniądze, możesz przechowywać liczbę centów jako int zamiast liczby dolarów jako float).

BigDecimal może dokładnie przechowywać dowolną liczbę, która ma skończoną liczbę cyfr w podstawy 10 i numery rund że NIE (tak, trzy trzecie nie są jedną całość).

Racjonalnie może dokładnie przechowywać dowolną liczbę wymierną i nie może w ogóle przechowywać liczb niewymiernych.

0

Ponieważ wykonywana jest matematyka zmiennoprzecinkowa, liczba zwrócona jest tym, czego komputer używa do precyzji.

Jeśli chcesz uzyskać bliższą odpowiedź, z zadaną precyzją, po prostu wielokrotność liczby zmiennoprzecinkowej (np. O 100), przekonwertuj ją na int, zrób matematykę, a następnie podziel.

Istnieją inne rozwiązania, ale uważam to za najprostsze, ponieważ zaokrąglanie zawsze wydaje mi się trochę niestosowne.

ten został poproszony przed tutaj, może warto spojrzeć na niektóre odpowiedzi podano wcześniej, takie jak ten: Dealing with accuracy problems in floating-point numbers

6

Problemem jest to, że niektóre numery możemy łatwo napisać w systemie dziesiętnym nie ma dokładnej reprezentacji w danym formacie zmiennoprzecinkowym zaimplementowanym przez bieżący sprzęt. Nieformalny sposób na stwierdzenie, że wszystkie liczby całkowite, ale nie wszystkie frakcje, ponieważ zwykle przechowujemy frakcje o wykładniku 2**e. Masz więc 3 możliwości:

  1. Odpowiednio zaokrąglić. Niezaokrąglony wynik jest zawsze naprawdę bardzo blisko, więc zaokrąglony wynik jest niezmiennie "doskonały". Właśnie to robi JavaScript i wiele osób nawet nie zdaje sobie sprawy, że JS robi wszystko w zmiennoprzecinkowy sposób.

  2. Zastosowanie stałej arytmetyki. Ruby naprawdę czyni to naprawdę łatwym; jest to jeden z niewielu języków, które płynnie przesuwają się do Class Bignum z Fixnum, gdy liczby są większe.

  3. Użyj klasy, która ma na celu rozwiązanie tego problemu, jak BigDecimal

Aby spojrzeć na problem bardziej szczegółowo, możemy spróbować do reprezentowania „7,3” w formacie binarnym. 7 część jest łatwa, 111, ale jak to zrobić .3? 111,1 wynosi 7,5, jest zbyt duży, 111,01 jest 7,25, zbliża się.Okazuje się, że 111.010011 to "najbliższy numer mniejszy", 7.296875, a kiedy próbujemy wypełnić brakujący .003125, w końcu dowiadujemy się, że to tylko 111.010011001100110011 ... na zawsze, nie można go reprezentować w naszym wybranym kodowaniu w skończonym łańcuchu bitów .

1

Warto zauważyć, że liczba, która ma kilka miejsc dziesiętnych w jednej bazie, może zwykle mieć bardzo dużą liczbę miejsc po przecinku w innej. Na przykład, zajmuje nieskończoną liczbę miejsc dziesiętnych, aby wyrazić 1/3 (= 0.3333 ...) w bazie 10, ale tylko jeden dziesiętny w bazie 3. Podobnie, zajmuje wiele miejsc po przecinku, aby wyrazić liczbę 1/10 (= 0.1) w bazie 2.