truncar redondear parte multiplicacion exponente entero entera decimales python integer division floating-accuracy integer-division

parte - redondear en python



¿Es la división entera siempre igual al piso de la división regular? (3)

Para cocientes grandes, la división entera ( // ) no parece ser necesariamente igual al piso de la división regular ( math.floor(a/b) ).

Según los documentos de Python ( https://docs.python.org/3/reference/expressions.html - 6.7),

la división del piso de los números enteros da como resultado un número entero; el resultado es el de la división matemática con la función ''piso'' aplicada al resultado.

Sin embargo,

math.floor(648705536316023400 / 7) = 92672219473717632 648705536316023400 // 7 = 92672219473717628

''{0:.10f}''.format(648705536316023400 / 7) produce ''92672219473717632.0000000000'', pero los dos últimos dígitos de la parte decimal deben ser 28 y no 32.


Es posible que esté tratando con valores integrales que son demasiado grandes para expresarse exactamente como flotadores. Su número es significativamente mayor que 2 ^ 53, que es donde los espacios entre los dobles de punto flotante adyacentes comienzan a ser mayores que 1 . Así que pierdes algo de precisión al hacer la división de punto flotante.

La división entera, por otro lado, se calcula exactamente.


La razón por la que los cocientes en su caso de prueba no son iguales es que en el caso math.floor(a/b) , el resultado se calcula con aritmética de punto flotante (IEEE-754 de 64 bits), lo que significa que hay una precisión máxima. El cociente que tiene allí es mayor que el límite de 2 53 por encima del cual el punto flotante ya no es exacto hasta la unidad.

Sin embargo, con la división de enteros, Python utiliza su rango ilimitado de enteros, y el resultado es correcto.

Ver también "Semántica de la verdadera división" en PEP 238 :

Tenga en cuenta que para argumentos int y largos, la división verdadera puede perder información; esto está en la naturaleza de la verdadera división (siempre que los racionales no estén en el lenguaje). Los algoritmos que usan conscientemente largos deben considerarse // , ya que la verdadera división de largos no retiene más de 53 bits de precisión (en la mayoría de las plataformas).


Su problema es que, a pesar del hecho de que "/" a veces se denomina "operador de división verdadera" y su nombre de método es __truediv__ , su comportamiento en enteros no es "división matemática verdadera". En su lugar, produce un resultado de punto flotante que inevitablemente tiene una precisión limitada.

Para números suficientemente grandes, incluso la parte integral de un número puede sufrir errores de redondeo de punto flotante. Cuando 648705536316023400 se convierte en un flotador de Python (doble IEEE) se redondea a 648705536316023424 1 .

Parece que no puedo encontrar documentación autorizada sobre el comportamiento exacto de los operadores en los tipos incorporados en Python actual. El PEP original que introdujo la característica indica que "/" equivale a convertir los enteros en coma flotante y luego realizar la división en coma flotante. Sin embargo, una prueba rápida en Python 3.5 muestra que ese no es el caso. Si fuera así, el siguiente código no produciría salida.

for i in range(648705536316023400,648705536316123400): if math.floor(i/7) != math.floor(float(i)/7): print(i)

Pero al menos para mí produce salida.

En cambio, me parece que Python está realizando la división en los números tal como se presentan y redondeando el resultado para que quepa en un número de punto flotante. Tomando un ejemplo de esa salida de programas.

648705536316123383 // 7 == 92672219473731911 math.floor(648705536316123383 / 7) == 92672219473731904 math.floor(float(648705536316123383) / 7) == 92672219473731920 int(float(92672219473731911)) == 92672219473731904

La biblioteca estándar de Python proporciona un tipo de Fracción y el operador de división para una Fracción dividida por un int realiza una "verdadera división matemática".

math.floor(Fraction(648705536316023400) / 7) == 92672219473717628 math.floor(Fraction(648705536316123383) / 7) == 92672219473731911

Sin embargo, debe tener en cuenta el potencial grave y las implicaciones de memoria de usar el tipo de Fracción. Recuerde que las fracciones pueden aumentar en el requerimiento de almacenamiento sin aumentar en magnitud.

Para probar aún más mi teoría de "un redondeo frente a dos", hice una prueba con el siguiente código.

#!/usr/bin/python3 from fractions import Fraction edt = 0 eft = 0 base = 1000000000010000000000 top = base + 1000000 for i in range(base,top): ex = (Fraction(i)/7) di = (i/7) fl = (float(i)/7) ed = abs(ex-Fraction(di)) ef = abs(ex-Fraction(fl)) edt += ed eft += ef print(edt/10000000000) print(eft/10000000000)

Y la magnitud de error promedio fue sustancialmente menor para realizar la división directamente que para convertir a flotar primero, apoyando el redondeo uno frente a la teoría dos.

1 Tenga en cuenta que la impresión de un flotador directamente no muestra su valor exacto, sino que muestra el número decimal más corto que se redondeará a ese valor (permitiendo la conversión sin pérdida de ida y vuelta de flotante a cadena y de vuelta a flotar).