3

Estoy siguiendo un curso de Python y un usuario ha hecho una pregunta que me resultó interesante.

Resulta que si en Python hacemos lo siguiente

def suma(num1, num2):
    return num1 + num2

print(suma(-4.2, 6))

El resultado es 1.799999999999999998, no es 1.8 como debería ser, así pasa con todos los decimales de -4 a excepción de -4.5, me resulta muy extraño ya que con -4.5 el resultado es correcto 5.5

Alguna explicación lógica al respecto?

1 Answers1

3

No es un problema de Python. Es un problema de la forma en que se representan los números no enteros en los computadores.

Python, al igual que cualquier otro lenguaje hoy día, utiliza un formato binario estandarizado, llamado IEEE-754 de doble precisión. Debido a la forma en que este formato guarda los números, hay multitud de ellos que no se pueden representar de forma exacta y en su lugar se guarda la mejor aproximación posible.

Para más detalles del por qué ocurre esto tienes varias respuestas muy buenas en la siguiente pregunta:

Un número de aspecto tan "inocente" como 4.2 no es representable de forma exacta, porque no es una suma de potencias de dos. Dicho de otra forma, si lo pasas a binario, tendrá infinitos decimales (periódicos), por lo que al almacenarlo en una cantidad fija de bits habrá que eliminar los que no quepan. En cambio el 4.5 sí es representable de forma exacta, porque 0.5 es una potencia de dos (es 1/2, o sea, 2 elevado a -1). En binario, 4.5 es 100.1 que tiene un número finito de cifras.

Este problema con la representación de punto flotante es importante para aplicaciones de tipo bancario, ya que la mayoría de cantidades con céntimos no son representables de forma exacta en IEEE-754, salvo 0.5 0.25 y 0.75 (pues estas son las únicas expresables como sumas de potencias de 1/2). Los errores de redondeo se pueden ir acumulando a medida que se suman estos números, y en las aplicaciones bancarias estos errores son dinero.

Para estos casos Python proporciona el tipo Decimal, que puede almacenar los decimales con la precisión que tú le indiques (pues para ellos ya no usa coma flotante). Por ejemplo:

from decimal import Decimal

def suma(num1, num2):
    return num1 + num2

a = Decimal("-4.2")
b = Decimal("6.0")
print(suma(a,b))

Verás que ahora ya sale 1.8.

No obstante son bastante farragosos de utilizar, como puedes comprobar, y deben inicializarse desde una cadena, nunca desde un flotante, pues aunque está permitido, si hubiera hecho a = Decimal(-4.2), en a no se guardará -4.2 puesto que el flotante que le he pasado ya no es -4.2 debido al error de redondeo de IEEE (sería '-4.20000000000000017763568394002504646778106689453125', con lo que no habría servido de nada convertirlo a Decimal())

abulafia
  • 53,696
  • 3
  • 45
  • 80