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.
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
Próbowałem zarówno C, jak i Octave i wszystkie one wypisują '-67.664062', tak samo jak Python. – kennytm
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