Qual è la ragione della differenza tra la divisione di numeri interi e la conversione da float a int in Python?


52

Ho notato di recente che int()arrotonda un galleggiante verso 0, mentre la divisione intera arrotonda un galleggiante verso il suo piano.

per esempio:

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

Ho letto la documentazione che specifica:

classe int (x, base = 10)

Restituisce un oggetto intero costruito da un numero o da una stringa x oppure restituisce 0 se non viene fornito alcun argomento. Se x è un numero, restituisce x. int (). Per i numeri in virgola mobile, questo si tronca verso zero.

e:

divisione del pavimento

Divisione matematica che arrotonda per difetto all'intero più vicino. L'operatore divisione pavimento è //. Ad esempio, l'espressione 11 // 4 restituisce 2 in contrasto con 2,75 restituito da float true division. Si noti che (-11) // 4 è -3 perché è -2,75 arrotondato verso il basso. Vedi PEP 238.

Ma mi sembra illogico che 2 operazioni simili (divisione float in numero intero) debbano restituire risultati diversi.

C'è qualche motivazione per le differenze tra le funzioni?

Grazie.


Risposte:


61

Consistenza.

Dovrai seguire alcune spiegazioni molto basilari e apparentemente irrilevanti per capirlo.

A scuola hai imparato la divisione con un resto. E hai fatto calcoli in questo modo:

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)

Più tardi, hai imparato le divisioni per i numeri reali:

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)

Fino a questo punto, potresti crederci x // 4e int(x/4)dare sempre lo stesso risultato. Questa è la tua attuale comprensione della situazione.

Tuttavia, dai un'occhiata a ciò che accade nella divisione intera: il numero dietro R scorre da 3, 2, 1 a 0 e quindi ricomincia: 3, 2, 1, 0. Il numero davanti a R diminuisce ogni 4 ° passaggio.

Quindi, come andrà avanti?

 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

Allo stesso tempo, la divisione dei numeri reali ci dà:

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

Ecco perché -1 // 4dà -1 ma int(-1/4)dà 0.

C'è qualche motivazione per le differenze tra le funzioni?

Bene, servono a diversi scopi: //fa parte di un calcolo intero con i resti e int()ti dà la parte davanti al .di un'operazione di numero reale.

Decidi tu cosa vuoi calcolare, quindi decidi quale operatore utilizzare in Python per ottenere il risultato corretto.

Buona domanda. Continua ad imparare.


11
In pratica, questo consente un trucco: se hai -1 caramelle e lo dai a 4 amici, allora resteranno 3 caramelle. Fantastico, vero? Hai solo bisogno di scoprire come possedere -1 dolci.
Thomas Weller,

1
Crea una sorta di coerenza, per quanto io comprenda la motivazione nell'aggiungere l' //operatore in Python 3 per evitare di forzare l'uso di int (float). In caso contrario, quando dovrei scegliere di implementare usando int()e quando dovrei implementare usando//
IsaacDj

1
Ok, allora è solo un presupposto sbagliato. Non è niente di male, a patto che tu verifichi la correttezza delle tue assunzioni, che probabilmente fallisce nel 50% dei casi (almeno lo fa per me). Ho aggiunto alcune parole al riguardo nella risposta.
Thomas Weller,

2
@IsaacDj potresti voler leggere questo per la storia dietro l'operatore "divisione del pavimento".
bruno desthuilliers,

1
@EricLippert: non credo sia bizzarro. Non possiamo supporre che un'operazione in perdita fornisca lo stesso risultato di un'operazione precisa. Parlato nel codice: Math.Floor(3.23) != -Math.Floor(-3.23)per lo stesso motivo -((-x)//y)non è necessario che sia uguale x//y.
Thomas Weller,

4

Direi che la tua osservazione secondo cui queste 2 operazioni dovrebbero essere intuitivamente simili è prevista poiché, in numeri positivi, si comportano in modo identico. Ma se guardi alle loro origini (una viene dalla matematica e l'altra dall'informatica), allora ha più senso il loro diverso comportamento.

Puoi guardare dietro i concetti:

  • Divisione del piano ovvero la funzione del piano applicata alla divisione matematica
  • Conversione del tipo / Casting del tipo

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

I) Divisione del pavimento, ovvero la funzione del pavimento applicata alla divisione matematica

La funzione floor è un concetto molto consolidato in matematica.

Da mathworld.wolfram :

La funzione floor | _ x_ |, chiamata anche la più grande funzione intera o valore intero (Spanier e Oldham 1987), fornisce il numero intero più grande minore o uguale a x. Il nome e il simbolo della funzione floor sono stati coniati da KE Iverson (Graham et al. 1994)

Quindi la divisione del piano non è altro che la funzione del piano applicata alla divisione matematica. Il comportamento è molto chiaro, "matematicamente preciso".

II) Conversione del tipo / Casting di tipo

Da Wikipedia :

Nell'informatica, la conversione dei tipi, la fusione dei tipi, la coercizione dei tipi e la manipolazione dei tipi sono modi diversi di cambiare un'espressione da un tipo di dati a un altro.

Nella maggior parte dei linguaggi di programmazione, il modulo di fusione float su intero viene applicato arrotondando la regola (quindi esiste una convenzione):

  • Arrotondamento verso 0 - arrotondamento diretto verso zero (noto anche come troncamento)

Regola di arrotondamento secondo IEEE 754 .


Quindi, in altre parole, la ragione della differenza tra la divisione intera e la conversione da float a int in python è matematica, ecco alcuni pensieri di Guido van Rossum (suppongo di non doverlo presentare: D) (dal blog La storia di Python, articolo "Why Python's Integer Division Floors" )

Questo disturba alcune persone, ma c'è una buona ragione matematica. L'operazione di divisione di interi (//) e il suo fratello, l'operazione di modulo (%), vanno insieme e soddisfano una bella relazione matematica (tutte le variabili sono numeri interi):

a / b = q con resto r

tale che

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

(supponendo che aeb siano> = 0).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.