Wyobrażam sobie, że jest to klasyczne pytanie precyzyjne zmiennoprzecinkowe, ale staram się owinąć głowę tym wynikiem, działając 1//0.01
w wydajności Pythona 3.7.5 99
.
Wyobrażam sobie, że jest to oczekiwany wynik, ale czy jest jakiś sposób, aby zdecydować, kiedy jest bezpieczniejszy w użyciu int(1/f)
niż 1//f
?
round()
i nigdy//
lubint()
. Powiązane pytanie dotyczy porównania zmiennoprzecinkowego, które nie ma nic wspólnego ze obcięciem i nie ma takiej łatwej poprawki.Odpowiedzi:
Gdyby to był podział z liczbami rzeczywistymi,
1//0.01
wynosiłby dokładnie 100. Ponieważ są to przybliżenia zmiennoprzecinkowe,0.01
jest on jednak nieco większy niż 1/100, co oznacza, że iloraz jest nieco mniejszy niż 100. To jest ta 99. wartość do 99.źródło
Przyczyny tego wyniku są takie, jak podajesz i zostały wyjaśnione w Czy matematyka zmiennoprzecinkowa jest zepsuta? i wiele innych podobnych pytań i odpowiedzi.
Kiedy znasz liczbę miejsc po przecinku licznika i mianownika, bardziej wiarygodnym sposobem jest pomnożenie tych liczb w pierwszej kolejności, aby można je było traktować jako liczby całkowite, a następnie wykonać na nich dzielenie liczb całkowitych:
Więc w twoim przypadku
1//0.01
należy przeliczyć najpierw na1*100//(0.01*100)
100.W bardziej ekstremalnych przypadkach nadal można uzyskać „nieoczekiwane” wyniki. Konieczne może być dodanie
round
wywołania licznika i mianownika przed wykonaniem podziału na liczby całkowite:Ale jeśli chodzi o pracę ze stałymi liczbami dziesiętnymi (pieniądze, centy), to rozważ pracę z centami jako jednostką , aby cała arytmetyka mogła być wykonana jako arytmetyka liczb całkowitych i podczas konwersji tylko do / z głównej jednostki pieniężnej (dolara) I / O.
Lub alternatywnie użyj biblioteki dziesiętnej, na przykład dziesiętnej , która:
źródło
Co trzeba wziąć pod uwagę to, że
//
jest tofloor
operator i jako taki powinien najpierw pomyśleć jak jeśli mają równe prawdopodobieństwo spadku w 100 jak w 99 (*) (ponieważ operacja będzie100 ± epsilon
zepsilon>0
warunkiem, że szanse na uzyskanie dokładnie 100,00 ..0 są bardzo niskie.)Możesz zobaczyć to samo ze znakiem minus,
i powinieneś być (nie) zaskoczony.
Z drugiej strony
int(-1/.01)
wykonuje najpierw podział, a następnie stosujeint()
liczbę, która nie jest podłogą, ale obcięciem w kierunku 0 ! co oznacza, że w takim przypadkuW związku z tym,
Zaokrąglenie dałoby TWOIM oczekiwany wynik dla tego operatora, ponieważ znowu błąd jest niewielki dla tych liczb.
(*) Nie twierdzę, że prawdopodobieństwo jest takie samo, po prostu mówię, że a priori, kiedy wykonujesz takie obliczenia z zmienną arytmetyką, która jest oszacowaniem tego, co otrzymujesz.
źródło
Liczby zmiennoprzecinkowe nie mogą dokładnie reprezentować większości liczb dziesiętnych, więc po wpisaniu literału zmiennoprzecinkowego faktycznie otrzymujesz przybliżenie tego literału. Przybliżenie może być większe lub mniejsze niż wpisana liczba.
Możesz zobaczyć dokładną wartość liczby zmiennoprzecinkowej, rzutując ją na Dziesiętny lub Ułamek.
Możemy użyć typu Frakcja, aby znaleźć błąd spowodowany przez nasz niedokładny literał.
Możemy również dowiedzieć się, jak szczegółowe są liczby zmiennoprzecinkowe o podwójnej precyzji o wartości około 100, używając nextafter z numpy.
Na podstawie tego możemy przypuszczać, że najbliższa liczba zmiennoprzecinkowa
1/0.01000000000000000020816681711721685132943093776702880859375
to w rzeczywistości dokładnie 100.Różnica między
1//0.01
iint(1/0.01)
jest zaokrąglenie. 1 // 0,01 zaokrągla dokładny wynik w dół do następnej liczby całkowitej w jednym kroku. Otrzymujemy wynik 99.int (1 / 0,01) z drugiej strony zaokrągla w dwóch etapach, najpierw zaokrągla wynik do najbliższej liczby zmiennoprzecinkowej podwójnej precyzji (czyli dokładnie 100), a następnie zaokrągla tę liczbę zmiennoprzecinkową w dół do następnej liczby całkowitej (która jest ponownie dokładnie 100).
źródło
int(0.9) == 0
iint(-0.9) == 0
Jeśli wykonasz następujące czynności
Dane wyjściowe będą:
W ten sposób jest on reprezentowany wewnętrznie, więc zaokrąglenie w dół
//
da99
źródło
Decimal(0.01)
jesteś za późno, błąd już się pojawił, zanim zadzwoniszDecimal
. Nie jestem pewien, jak to jest odpowiedź na pytanie ... Musisz najpierw obliczyć dokładną wartość 0,01Decimal(1) / Decimal(100)
, tak jak pokazałem w mojej odpowiedzi.