python floating-point decimal python-2.4

Mal en el python decimal/float



floating-point python-2.4 (2)

Tengo una gran cantidad de código de Python que trata de manejar números con una precisión de 4 decimales y estoy atascado con Python 2.4 por muchas razones. El código hace cálculos matemáticos muy simplistas (es un código de administración de créditos que toma o agrega créditos en su mayoría)

Ha mezclado el uso de flotación y decimal (MySQLdb devuelve objetos decimales para los tipos DECIMAL de SQL). Después de varios errores extraños que surgen del uso, he encontrado que la causa raíz de todos son algunos lugares en el código que flotan y los decimales se están comparando.

Llegué a casos como este:

>>> from decimal import Decimal >>> max(Decimal(''0.06''), 0.6) Decimal("0.06")

Ahora mi temor es que no pueda detectar todos los casos en el código. (un programador normal seguirá haciendo x> 0 en lugar de x> Decimal (''0.0000'') y es muy difícil evitarlo)

He creado un parche (inspirado en las mejoras del paquete decimal en Python 2.7).

import decimal def _convert_other(other): """Convert other to Decimal. Verifies that it''s ok to use in an implicit construction. """ if isinstance(other, Decimal): return other if isinstance(other, (int, long)): return Decimal(other) # Our small patch begins if isinstance(other, float): return Decimal(str(other)) # Our small patch ends return NotImplemented decimal._convert_other = _convert_other

Simplemente lo hago en una biblioteca de carga muy temprana y cambiará el comportamiento del paquete decimal permitiendo la conversión de flotación a decimal antes de las comparaciones (para evitar golpear el objeto predeterminado de python con la comparación de objetos).

Usé específicamente "str" ​​en lugar de "repr", ya que corrige algunos de los casos de redondeo de float. P.ej

>>> Decimal(str(0.6)) Decimal("0.6") >>> Decimal(repr(0.6)) Decimal("0.59999999999999998")

Ahora mi pregunta es: ¿Me estoy perdiendo algo aquí? ¿Es esto bastante seguro? ¿O estoy rompiendo algo aquí? (Estoy pensando que los autores del paquete tenían razones muy fuertes para evitar tanto las flotaciones)


Creo que quieres raise NotImplementedError() lugar de return NotImplemented , para comenzar.

Lo que estás haciendo se llama "parches de monos", y está bien que lo hagas, siempre que sepas lo que estás haciendo, estés al tanto de las consecuencias y estés de acuerdo con esas consecuencias. En general, se limita a corregir un error, o algún otro cambio en el que sepa que la alteración del comportamiento sigue siendo correcta y compatible con versiones anteriores.

En este caso, debido a que está parchando una clase, puede cambiar el comportamiento fuera de los casos en que lo usa. Si otra biblioteca usa decimal, y de alguna manera se basa en el comportamiento predeterminado, podría causar errores sutiles. El problema es que realmente no lo sabe a menos que audite todo su código, incluidas las dependencias, y encuentre todos los sitios de llamadas.

Básicamente - hazlo bajo tu propio riesgo.

Personalmente, me resulta más tranquilizador arreglar todo mi código, agregar pruebas y hacer que sea más difícil hacer lo incorrecto (por ejemplo, usar clases de envoltorio o funciones de ayuda). Otro enfoque sería instrumentar su código con su parche para encontrar todos los sitios de llamadas, luego regresar y corregirlos.

Editar: supongo que debo agregar que la razón probable por la que evitaron las flotaciones es que las flotaciones no pueden representar con precisión todos los números, lo cual es importante si se trata de dinero.


Hay muy buenas razones para evitar flotadores. Con los flotadores, no puede hacer comparaciones confiables como ==,>, <etc. debido al ruido de punto flotante. Con cualquier operación de punto flotante acumulas ruido. Comienza con dígitos muy pequeños al final, por ejemplo, 1.000 ... 002 pero eventualmente puede acumularse como 1.0000000453436.

El uso de str () puede funcionar para usted si no realiza tantos cálculos de punto flotante, pero si realiza muchos cálculos, el ruido de punto flotante finalmente será lo suficientemente grande como para que str () le dé la respuesta incorrecta.

En resumen, si (1) no hace tantos cálculos de punto flotante, o (2) no necesita hacer comparaciones como ==,>, <etc., entonces podría estar bien.

Si desea estar seguro, elimine todo el código de punto flotante.