Qual é a razão da diferença entre divisão inteira e conversão float para int em python?


52

Recentemente, notei que int()arredonda um flutuador em direção a 0, enquanto a divisão inteira arredonda um flutuador em direção ao seu piso.

por exemplo:

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

Eu li a documentação que especifica:

classe int (x, base = 10)

Retorne um objeto inteiro construído a partir de um número ou string x ou retorne 0 se nenhum argumento for fornecido. Se x é um número, retorne x. int (). Para números de ponto flutuante, isso é truncado para zero.

e:

divisão de piso

Divisão matemática que arredonda para baixo para o número inteiro mais próximo. O operador da divisão de piso é //. Por exemplo, a expressão 11 // 4 é avaliada como 2 em contraste com os 2,75 retornados pela divisão verdadeira float. Observe que (-11) // 4 é -3 porque é -2,75 arredondado para baixo. Ver PEP 238.

Mas parece ilógico para mim que 2 operações semelhantes (divisão de flutuação para inteiro) devam retornar resultados diferentes.

Existe alguma motivação para as diferenças entre as funções?

Obrigado.


Respostas:


61

Consistência.

Você precisará seguir algumas explicações muito básicas e aparentemente irrelevantes para entendê-lo.

Na escola, você aprendeu a divisão com o restante. E você fez cálculos como este:

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)

Mais tarde, você aprendeu divisões para números reais:

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)

Até esse ponto, você pode acreditar nisso x // 4e int(x/4)sempre fornecer o mesmo resultado. Essa é a sua compreensão atual da situação.

No entanto, observe o que acontece na divisão inteira: o número atrás de R alterna entre 3, 2, 1 e 0 e reinicia: 3, 2, 1, 0. O número na frente de R decresce a cada 4º passo.

Então, como vai continuar?

 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

Ao mesmo tempo, a divisão de números reais nos fornece:

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

É por isso -1 // 4 dá -1, mas int(-1/4)dá 0.

Existe alguma motivação para as diferenças entre as funções?

Bem, eles servem a propósitos diferentes: //faz parte de um cálculo inteiro com restos eint() fornece a parte na frente do. de uma operação de número real.

Você decide o que deseja calcular e decide qual operador usar no Python para obter o resultado correto.

Boa pergunta. Continue aprendendo.


11
Na prática, isso permite um truque: se você tiver -1 doces e o distribuir para 4 amigos, restarão 3 doces. Ótimo, não é? Você só precisa descobrir como possuir -1 doces.
Thomas Weller

11
Ele cria algum tipo de consistência, mas até onde eu entendo a motivação em adicionar o //operador no python 3 é para evitar forçar o uso de int (float). Se este não for o caso, quando eu deveria optar por implementar usando int()e quando devo implementar usando//
IsaacDj

11
Ok, então é apenas uma suposição errada. Isso não é nada ruim, contanto que você teste suas suposições quanto à correção, o que provavelmente falha em 50% dos casos (pelo menos para mim). Eu adicionei algumas palavras sobre isso na resposta.
Thomas Weller

2
@IsaacDj, você pode ler isso para ver a história por trás do operador "divisão de chão".
bruno desthuilliers 12/12/19

11
@ EricLippert: Eu não acho isso bizarro. Não podemos assumir que uma operação com perdas forneça o mesmo resultado que uma operação precisa. Falado no código: Math.Floor(3.23) != -Math.Floor(-3.23)pelo mesmo motivo, -((-x)//y)não é necessário x//y.
Thomas Weller

4

Eu diria que sua observação de que essas duas operações devem ser intuitivamente semelhantes é esperada, pois em números positivos elas se comportam de forma idêntica. Mas se você olhar para as suas origens (uma vem da matemática e a outra da ciência da computação), então faz mais sentido o seu comportamento diferente.

Você pode olhar por trás dos conceitos:

  • Divisão de piso, também conhecida como função de piso, aplicada à divisão matemática
  • Conversão de tipo / conversão de tipo

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

I) Divisão de piso, também conhecida como função de piso, aplicada à divisão matemática

A função de piso é um conceito muito bem estabelecido em matemática.

Do mathworld.wolfram :

A função de piso | _ x_ |, também chamada de maior função inteira ou valor inteiro (Spanier e Oldham 1987), fornece o maior número inteiro menor ou igual a x. O nome e o símbolo da função de piso foram cunhados por KE Iverson (Graham et al. 1994)

Portanto, a divisão do piso nada mais é do que a função do piso aplicada à divisão matemática. O comportamento é muito claro, "matematicamente preciso".

II) Conversão de tipo / fundição de tipo

Da wikipedia :

Na ciência da computação, conversão de tipos, conversão de tipos, coerção de tipos e malabarismo de tipos são maneiras diferentes de alterar uma expressão de um tipo de dados para outro.

Na maioria das linguagens de programação, o formulário de conversão flutuante para inteiro é aplicado pela regra de arredondamento (portanto, existe uma convenção):

  • Arredondar para 0 - arredondamento direcionado para zero (também conhecido como truncamento)

Regra de arredondamento de acordo com a IEEE 754 .


Portanto, em outras palavras, a razão da diferença entre divisão inteira e conversão float para int em python é matemática, aqui estão alguns pensamentos de Guido van Rossum (acho que não preciso apresentá-lo: D) blog A história do Python, artigo "Por que a divisão inteira do Python flutua" )

Isso incomoda algumas pessoas, mas há uma boa razão matemática. A operação de divisão inteira (//) e seu irmão, a operação de módulo (%), caminham juntos e satisfazem um bom relacionamento matemático (todas as variáveis ​​são inteiras):

a / b = q com o restante r

de tal modo que

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

(assumindo que aeb sejam> = 0).

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.