¿Cuál es la razón de la diferencia entre la división de enteros y la conversión de flotante a int en python?

52

Recientemente he notado que int()redondea un flotador hacia 0, mientras que la división entera redondea un flotador hacia su piso.

por ejemplo:

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

He leído la documentación que especifica:

clase int (x, base = 10)

Devuelve un objeto entero construido a partir de un número o cadena x, o devuelve 0 si no se dan argumentos>. Si x es un número, devuelve x. int (). Para números de coma flotante, esto se trunca hacia cero.

y:

división de piso

División matemática que redondea al entero más cercano. El operador de división de piso es //. Por ejemplo, la expresión 11 // 4 se evalúa como 2 en contraste con el 2,75 devuelto por la división verdadera flotante. Tenga en cuenta que (-11) // 4 es -3 porque es -2.75 redondeado hacia abajo. Ver PEP 238.

Pero me parece ilógico que 2 operaciones similares (división flotante a entero) deban devolver resultados diferentes.

¿Hay alguna motivación para las diferencias entre las funciones?

Gracias.

IsaacDj
fuente
Enlace relacionado: python-history.blogspot.com/2010/08/…
dan04 12/12/2019

Respuestas:

61

Consistencia.

Tendrás que seguir algunas explicaciones muy básicas y aparentemente irrelevantes para entenderlo.

En la escuela has aprendido la división con un resto. Y has hecho 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)

Más tarde, has aprendido divisiones para números reales:

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)

Hasta este punto, puede creer eso x // 4y int(x/4)siempre dar el mismo resultado. Esa es su comprensión actual de la situación.

Sin embargo, eche un vistazo a lo que sucede en la división de enteros: el número detrás de R cambia de 3, 2, 1 a 0 y luego se reinicia: 3, 2, 1, 0. El número frente a R disminuye cada 4to paso.

Entonces, ¿cómo 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

Al mismo tiempo, la división de números reales nos da:

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

Es por eso que -1 // 4da -1 pero int(-1/4)da 0.

¿Hay alguna motivación para las diferencias entre las funciones?

Bueno, tienen diferentes propósitos: //es parte de un cálculo de enteros con restos y int()le da la parte frente a la .operación de un número real.

Tú decides lo que quieres calcular, luego decides qué operador usar en Python para obtener el resultado correcto.

Buena pregunta. Sigue aprendiendo.

Thomas Weller
fuente
11
En la práctica, esto permite un truco: si tienes -1 dulces y lo regalas a 4 amigos, quedarán 3 dulces. Genial, no? Solo necesita saber cómo poseer -1 dulces.
Thomas Weller
1
Sí crea algún tipo de coherencia, sin embargo, hasta donde entiendo, la motivación para agregar el //operador en python 3 es evitar el uso de int (float). Si este no es el caso, ¿cuándo debería elegir implementar usando int()y cuándo debo implementar usando//
IsaacDj
1
Ok, entonces es solo una suposición equivocada. Eso no es nada malo, siempre que pruebe sus suposiciones para verificar que sean correctas, lo que probablemente falla en el 50% de los casos (al menos lo hace para mí). Agregué algunas palabras al respecto en la respuesta.
Thomas Weller
2
@IsaacDj es posible que desee leer esto para la historia detrás del operador de "división de piso".
bruno desthuilliers
1
@EricLippert: No creo que sea extraño. No podemos suponer que una operación con pérdidas proporciona el mismo resultado que una operación precisa. Hablado en código: Math.Floor(3.23) != -Math.Floor(-3.23)por la misma razón -((-x)//y)no tiene por qué ser igual x//y.
Thomas Weller
4

Diría que se espera su observación de que esas 2 operaciones deberían ser intuitivamente similares ya que en números positivos se comportan de manera idéntica. Pero si nos fijamos en sus orígenes (uno proviene de las matemáticas y el otro de la informática), entonces tiene más sentido su comportamiento diferente.

Puedes mirar detrás de los conceptos:

  • División de piso, también conocida como la función de piso aplicada a la división matemática
  • Conversión de tipo / Fundición de tipo

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

I) División de piso, también conocida como la función de piso aplicada a la división matemática

La función de piso es un concepto muy bien establecido en matemáticas.

De mathworld.wolfram :

La función de piso | _ x_ |, también llamada la función entera más grande o valor entero (Spanier y Oldham 1987), da el número entero más grande menor o igual que x. El nombre y el símbolo de la función de piso fueron acuñados por KE Iverson (Graham et al. 1994)

Entonces, la división de piso no es más que la función de piso aplicada a la división matemática. El comportamiento es muy claro, "matemáticamente preciso".

II) Tipo de conversión / Tipo de fundición

De wikipedia :

En informática, la conversión de tipos, la conversión de tipos, la coerción de tipos y el malabarismo de tipos son diferentes formas de cambiar una expresión de un tipo de datos a otro.

En la mayoría de los lenguajes de programación, la forma de conversión flotante a entero se aplica mediante una regla de redondeo (por lo que existe una convención):

  • Redondear hacia 0: redondeo dirigido hacia cero (también conocido como truncamiento)

Regla de redondeo según IEEE 754 .


En otras palabras, la razón de la diferencia entre la división de enteros y la conversión de flotación a int en python es matemática, aquí hay algunos pensamientos de Guido van Rossum (supongo que no tengo que presentarlo: D) (del blog La historia de Python, artículo "Por qué los pisos de la división entera de Python" )

Esto molesta a algunas personas, pero hay una buena razón matemática. La operación de división de enteros (//) y su hermano, la operación de módulo (%), van juntas y satisfacen una buena relación matemática (todas las variables son enteras):

a / b = q con resto r

tal que

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

(suponiendo que ayb son> = 0).

Kederrac
fuente