Jaki jest powód różnicy między podziałem liczb całkowitych a konwersją typu float na int w pythonie?

52

Zauważyłem niedawno, że int()zaokrągla liczbę zmiennoprzecinkową w kierunku 0, podczas gdy dzielenie liczb całkowitych zaokrągla liczbę zmiennoprzecinkową w kierunku jej podłogi.

na przykład:

-7 // 2 = -4
int(-7/2) = -3

Przeczytałem dokumentację, która określa:

klasa int (x, podstawa = 10)

Zwraca liczbę całkowitą zbudowaną z liczby lub łańcucha x lub zwraca 0, jeśli nie podano> argumentów. Jeśli x jest liczbą, zwróć x. int (). W przypadku liczb zmiennoprzecinkowych powoduje to skrócenie do zera.

i:

podział podłogi

Podział matematyczny zaokrąglany w dół do najbliższej liczby całkowitej. Operatorem podziału podłogi jest //. Na przykład wyrażenie 11 // 4 ma wartość 2 w przeciwieństwie do 2,75 zwróconego przez zmiennoprzecinkowe dzielenie rzeczywiste. Zauważ, że (-11) // 4 to -3, ponieważ jest to -2,75 zaokrąglone w dół. Zobacz PEP 238.

Ale wydaje mi się nielogiczne, że 2 podobne operacje (dzielenie zmiennoprzecinkowe na liczbę całkowitą) powinny zwracać różne wyniki.

Czy jest jakaś motywacja do różnic między funkcjami?

Dziękuję Ci.

IsaacDj
źródło
Ważny
dan04

Odpowiedzi:

61

Konsystencja.

Będziesz musiał zastosować się do kilku bardzo podstawowych i pozornie nieistotnych wyjaśnień, aby to zrozumieć.

W szkole nauczyłeś się podziału z resztą. Dokonałeś takich obliczeń:

8 ÷ 4 = 2 R 0
7 ÷ 4 = 1 R 3
6 ÷ 4 = 1 R 2
5 ÷ 4 = 1 R 1
4 ÷ 4 = 1 R 0
3 ÷ 4 = 0 R 3
2 ÷ 4 = 0 R 2
1 ÷ 4 = 0 R 1
0 ÷ 4 = 0 R 0
        ^------ This is the result of x // 4
            ^-- This is the result of x % 4 (modulo)

Później nauczyłeś się podziałów na liczby rzeczywiste:

8 ÷ 4 = 2.0
7 ÷ 4 = 1.75
6 ÷ 4 = 1.5
5 ÷ 4 = 1.25
4 ÷ 4 = 1.0
3 ÷ 4 = 0.75
2 ÷ 4 = 0.5
1 ÷ 4 = 0.25
0 ÷ 4 = 0.0
        ^--- Note that the number in front of the . is int(x/4)

Do tego momentu możesz w to uwierzyć x // 4i int(x/4)zawsze dawać ten sam rezultat. To twoje obecne zrozumienie sytuacji.

Zobacz jednak, co dzieje się w dzieleniu liczb całkowitych: liczba za R zmienia się z 3, 2, 1 na 0, a następnie uruchamia się ponownie: 3, 2, 1, 0. Liczba przed R zmniejsza się co 4 stopień.

Jak to będzie dalej?

 8 ÷ 4 =  2 R 0
 7 ÷ 4 =  1 R 3
 6 ÷ 4 =  1 R 2
 5 ÷ 4 =  1 R 1
 4 ÷ 4 =  1 R 0
 3 ÷ 4 =  0 R 3
 2 ÷ 4 =  0 R 2
 1 ÷ 4 =  0 R 1
 0 ÷ 4 =  0 R 0
-1 ÷ 4 = -1 R 3
         ^------ We have to decrease now, because we already have 0 four times
              ^-- We have to restart the cycle at 3

Jednocześnie podział liczb rzeczywistych daje nam:

-1 ÷ 4 = -0.25
          ^----- There is still a 0 in front of the .

Dlatego -1 // 4daje -1, ale int(-1/4)daje 0.

Czy jest jakaś motywacja do różnic między funkcjami?

Cóż, służą one różnym celom: //są częścią obliczania liczb całkowitych z resztami i int()dają ci część przed .operacją na liczbach rzeczywistych.

Ty decydujesz, co chcesz obliczyć, a następnie decydujesz, którego operatora użyć w Pythonie, aby uzyskać poprawny wynik.

Dobre pytanie. Ucz się dalej.

Thomas Weller
źródło
11
W praktyce pozwala to na podstęp: jeśli masz -1 słodyczy i oddajesz je 4 znajomym, pozostaną 3 słodycze. Świetnie, prawda? Musisz tylko dowiedzieć się, jak posiadać -1 słodyczy.
Thomas Weller,
1
Tworzy to pewną spójność, ale o ile rozumiem, motywacja dodania //operatora w pythonie 3 ma na celu uniknięcie wymuszania użycia int (float). Jeśli tak nie jest, kiedy powinienem wdrożyć za pomocą, int()a kiedy powinienem za pomocą//
IsaacDj
1
Ok, to tylko błędne założenie. To nic złego, pod warunkiem, że przetestujesz swoje założenia pod kątem poprawności, co prawdopodobnie zawiedzie w 50% przypadków (przynajmniej tak mi się dzieje). Dodałem kilka słów na ten temat w odpowiedzi.
Thomas Weller,
2
@IsaacDj, możesz przeczytać to opowiadanie o operatorze „podziału podłogi”.
bruno desthuilliers
1
@EricLippert: Nie wydaje mi się, żeby to było dziwne. Nie możemy zakładać, że operacja stratna daje taki sam wynik jak operacja precyzyjna. Mówiony w kodzie: z Math.Floor(3.23) != -Math.Floor(-3.23)tego samego powodu -((-x)//y)nie musi być równy x//y.
Thomas Weller,
4

Powiedziałbym, że spodziewana jest twoja obserwacja, że ​​te 2 operacje powinny być intuicyjnie podobne, ponieważ przy liczbach dodatnich zachowują się identycznie. Ale jeśli spojrzysz na ich pochodzenie (jedno pochodzi z matematyki, a drugie z informatyki), to bardziej sensowne jest ich odmienne zachowanie.

Możesz poszukać pojęć:

  • Podział podłogi, czyli funkcja podłogi zastosowana do podziału matematycznego
  • Konwersja typu / casting typu

================================================== ================

I) Podział podłogi, czyli funkcja podłogi zastosowana do podziału matematycznego

Funkcja podłogi jest bardzo dobrze ugruntowaną koncepcją w matematyce.

From mathworld.wolfram :

Funkcja floor | _ x_ |, zwana także największą funkcją całkowitą lub wartością całkowitą (Spanier i Oldham 1987), daje największą liczbę całkowitą mniejszą lub równą x. Nazwa i symbol funkcji podłogi zostały wymyślone przez KE Iversona (Graham i in. 1994)

Podział podłogi jest więc niczym więcej niż funkcją podłogi zastosowaną do podziału matematycznego. Zachowanie jest bardzo jasne, „matematycznie precyzyjne”.

II) Konwersja typu / casting typu

Z wikipedii :

W informatyce konwersja typów, rzutowanie typów, wymuszanie typów i żonglowanie typami to różne sposoby zmiany wyrażenia z jednego typu danych na inny.

W większości języków programowania forma rzutowania float na liczba całkowita jest stosowana przez regułę zaokrąglania (istnieje więc konwencja):

  • Zaokrąglanie w kierunku 0 - ukierunkowane zaokrąglanie w kierunku zera (znane również jako obcinanie)

Reguła zaokrąglania zgodna z IEEE 754 .


Innymi słowy, przyczyną różnicy między dzieleniem liczb całkowitych a konwersją liczb zmiennoprzecinkowych do int w pythonie jest matematyka, oto kilka myśli Guido van Rossuma (chyba nie muszę go przedstawiać: D) (z blog Historia Pythona, artykuł „Dlaczego podłogi Integer Division Pythona” )

Niepokoi to niektórych ludzi, ale istnieje dobry powód matematyczny. Operacja dzielenia liczb całkowitych (//) i jej rodzeństwo, operacja modulo (%) idą w parze i spełniają ładny związek matematyczny (wszystkie zmienne są liczbami całkowitymi):

a / b = q z resztą r

takie, że

b * q + r = a i 0 <= r <b

(zakładając, że aib są> = 0).

kederrac
źródło