Me imagino que esta es una pregunta clásica de precisión de coma flotante, pero estoy tratando de entender este resultado, ejecutando 1//0.01
en Python 3.7.5 rendimientos 99
.
Me imagino que es un resultado esperado, pero ¿hay alguna forma de decidir cuándo es más seguro usarlo en int(1/f)
lugar de hacerlo 1//f
?
round()
y nunca//
oint()
. La pregunta vinculada es acerca de la comparación flotante no tiene nada que ver con el truncamiento y no hay una solución tan fácil.Respuestas:
Si se tratara de una división con números reales,
1//0.01
sería exactamente 100. Sin embargo, dado que son aproximaciones de punto flotante,0.01
es un poco más grande que 1/100, lo que significa que el cociente es un poco más pequeño que 100. Es este valor de 99. algo que luego se anula a 99.fuente
Las razones de este resultado son como usted dice, y se explican en ¿Se han roto las matemáticas de coma flotante? y muchas otras preguntas y respuestas similares.
Cuando conoce el número de decimales de numerador y denominador, una forma más confiable es multiplicar esos números primero para que puedan tratarse como enteros y luego realizar una división de enteros en ellos:
Entonces, en su caso,
1//0.01
debe convertirse primero a1*100//(0.01*100)
cuál es 100.En casos más extremos, aún puede obtener resultados "inesperados". Puede ser necesario agregar una
round
llamada al numerador y al denominador antes de realizar la división entera:Pero, si se trata de trabajar con decimales fijos (dinero, centavos), entonces considere trabajar con centavos como unidad , de modo que toda la aritmética se pueda hacer como aritmética de enteros, y solo se convierta a / desde la unidad monetaria principal (dólar) al hacer E / S
O, alternativamente, use una biblioteca para decimales, como decimal , que:
fuente
Lo que hay que tener en cuenta es que
//
es elfloor
operador y, como tal, debe pensar primero como si tiene la misma probabilidad de caer en 100 como en 99 (*) (debido a que la operación será100 ± epsilon
conepsilon>0
la condición de que las posibilidades de conseguir exactamente 100.00 ..0 son extremadamente bajos.)Puedes ver lo mismo con un signo menos,
y deberías estar tan (des) sorprendido.
Por otro lado,
int(-1/.01)
realiza primero la división y luego aplicaint()
el número, ¡que no es el piso sino un truncamiento hacia 0 ! lo que significa que en ese caso,por lo tanto,
Sin embargo, redondear le daría SU el resultado esperado para este operador porque, nuevamente, el error es pequeño para esas cifras.
(*) No digo que la probabilidad sea la misma, solo digo que a priori cuando realizas un cálculo con aritmética flotante que es una estimación de lo que estás obteniendo.
fuente
Los números de coma flotante no pueden representar la mayoría de los números decimales exactamente, por lo que cuando escribe un literal de coma flotante realmente obtiene una aproximación de ese literal. La aproximación puede ser mayor o menor que el número que escribió.
Puede ver el valor exacto de un número de coma flotante convirtiéndolo en decimal o fracción.
Podemos usar el tipo Fracción para encontrar el error causado por nuestro literal inexacto.
También podemos descubrir qué tan granulares son los números de coma flotante de precisión doble alrededor de 100 usando nextafter de numpy.
A partir de esto, podemos suponer que el número de coma flotante más cercano
1/0.01000000000000000020816681711721685132943093776702880859375
es de hecho exactamente 100.La diferencia entre
1//0.01
yint(1/0.01)
es el redondeo. 1 // 0.01 redondea el resultado exacto al siguiente número entero en un solo paso. Entonces obtenemos un resultado de 99.int (1 / 0.01) por otro lado redondea en dos etapas, primero redondea el resultado al número de punto flotante de precisión doble más cercano (que es exactamente 100), luego redondea ese número de punto flotante al siguiente entero (que es de nuevo exactamente 100).
fuente
int(0.9) == 0
yint(-0.9) == 0
Si ejecuta lo siguiente
El resultado será:
Así es como se representa internamente, por lo que redondearlo hacia abajo
//
dará99
fuente
Decimal(0.01)
llegas demasiado tarde, el error ya apareció antes de llamarDecimal
. No estoy seguro de cómo se trata de una respuesta a la pregunta ... Primero debe calcular un preciso 0.01 conDecimal(1) / Decimal(100)
, como lo mostré en mi respuesta.