2016-08-27 11 views
5

Przekazuję kod MATLAB do Pythona 3.5.1 i znalazłem problem zaokrąglania float.Kryteria zaokrąglania/zaokrąglania w Pythonie

W programie MATLAB, następująca liczba jest zaokrąglana się do 6. miejsca po przecinku:

fprintf(1,'%f', -67.6640625); 
-67.664063 

W Pythonie, z drugiej strony, następująca liczba jest zaokrąglana off do 6. miejsca po przecinku:

print('%f' % -67.6640625) 
-67.664062 

Co ciekawe, jeśli liczba jest „-67,6000625”, to jest zaokrąglana w góręnawet w Pythonie:

print('%f' % -67.6000625) 
-67.600063 

... Dlaczego tak się dzieje? Jakie są kryteria zaokrąglania/w górę w Pythonie? (Wierzę, że ma to coś wspólnego z obsługą wartości szesnastkowych.)

Co ważniejsze, jak mogę zapobiec tej różnicy? Mam na celu stworzenie kodu Pythona, który może odtwarzać dokładnie takie same wyniki, jak produkuje MATLAB.

+2

Byłem zaciekawiony, więc również podniosłem to zachowanie. To, co dostałem, było dziwne. 'np.around (a, 6)' (przy czym twoja pierwsza wartość) daje -66,664062000000001. Druga wartość, której próbujesz, wynosi: -66,600061999999994. [Oficjalna dokumentacja Pythona] (https://docs.python.org/3/tutorial/floatingpoint.html) wyjaśnia to zachowanie. – Vinay87

+2

Próbowałem zarówno C, jak i Octave i wszystkie one wypisują '-67.664062', tak samo jak Python. – kennytm

+3

Biorąc pod uwagę, jak trudne były zawsze porównania zmiennoprzecinkowe (np. 3.0 * 2.2! = 2.0 * 3.3), zacznę od zakwestionowania wykonalności projektu. Najlepszą praktyką porównywania dwóch zmiennych jest zawsze porównywanie różnicy z pewną tolerancją. – jadsq

Odpowiedz

4

Przyczyna zachowania python ma związek z tym, jak liczby zmiennoprzecinkowe są przechowywane w komputerze, oraz standardowe reguły zaokrąglania zdefiniowane przez IEEE, które określają standardowe formaty liczb i operacje matematyczne używane na prawie wszystkich współczesnych komputerach.

Potrzeba efektywnego przechowywania liczb w systemie binarnym na komputerze doprowadziła komputery do używania liczb zmiennoprzecinkowych. Numery te są łatwe w obsłudze dla procesorów, ale mają tę wadę, że wiele liczb dziesiętnych cannot be exactly represented. Powoduje to, że liczby czasami są trochę poniżej tego, jak sądzimy, że powinny być.

Sytuacja staje się nieco jaśniejsze jeśli rozszerzymy wartości w Pythonie, zamiast ich obcinania:

>>> print('%.20f' % -67.6640625) 
-67.66406250000000000000 
>>> print('%.20f' % -67.6000625) 
-67.60006250000000704858 

Więc jak widać, -67.6640625 to numer, który może być dokładnie reprezentowana, ale -67.6000625 ISN” t, w rzeczywistości jest trochę większy. Domyślny tryb zaokrąglania defined by the IEEE stanard dla liczb zmiennoprzecinkowych mówi, że cokolwiek powyżej 5 powinno zostać zaokrąglone w górę, wszystko poniżej powinno zostać zaokrąglone w dół. Tak więc w przypadku -67.6000625, jest to faktycznie 5 plus niewielka ilość, więc jest zaokrąglana w górę. Jednak w przypadku -67.6640625 jest dokładnie równa pięciu, więc obowiązuje zasada tiebreakbreak. Domyślna reguła rozstrzygająca jest zaokrąglana do najbliższej liczby parzystej. Ponieważ 2 jest najbliższym numerem zdarzenia, zaokrągla się do dwóch.

Tak więc Python stosuje podejście zalecane przez standard zmiennoprzecinkowy. Pytanie brzmi więc, dlaczego twoja wersja MATLAB nie wykonuje tego w postaci. Próbowałem go na moim komputerze z 64-bitowym MATLAB R2016a i mam taki sam wynik jak w Pythonie:

>> fprintf(1,'%f', -67.6640625) 
-67.664062>> 

Więc wydaje się, że MATLAB było, w pewnym momencie, przy użyciu innej metody zaokrąglania (może niestandardowym podejście, być może jedna z alternatyw podanych w standardzie), i od tamtej pory zmieniły się na takie same zasady jak wszyscy inni.

+0

Wow, dobrze wyjaśnione. Jestem całkowicie przekonany. Tak, używam MATLAB R2011b, więc trochę starszego. Myślałem, że MATLAB ma zawsze rację, ale myliłem się. Jedna dobra rzecz to to, że nie muszę naprawiać mojego kodu Pythona dla tego problemu. ;-) Dziękuję Ci bardzo! – IanHacker