es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Matemáticas en Django ORM y SQLite: decimal ignorado si el número es redondo, resultado correcto si no es redondo.

En una consulta compleja, tengo una anotación como esta:

result.annotate(test=ExpressionWrapper(485.00 / F('period_duration'), output_field=DecimalField()))

Esto me devuelve el resultado correcto:

Decimal('8.01917989417989E-10')

Sin embargo, si reemplazo 485.00 con 485:

result.annotate(test=ExpressionWrapper(485 / F('period_duration'), output_field=DecimalField()))

Obtengo:

Decimal('0')

Esto no sería un problema si no fuera porque 485 también proviene de un campo llamado “value”. Mi consulta se ve así:

result.annotate(test=ExpressionWrapper(F('value') / F('period_duration'), output_field=DecimalField()))

“Value” es un MoneyField, básicamente un envoltorio elegante alrededor de DecimalField.

Puedo obligar a mis usuarios a siempre usar decimales adecuados (485.00 en lugar de 485), lo cual sería un diseño deficiente, pero… incluso entonces, si el valor en la base de datos es 485.00, Django se comporta como si fuera 485 y devuelve 0 al realizar una operación de punto flotante.

He intentado realizar un Cast del valor a un DecimalField, sin éxito:

result.annotate(res=ExpressionWrapper(
    Cast('value', output_field=DecimalField()) / F('period_duration'), output_field=DecimalField()
)).last().res

El resultado siempre es:

Decimal('0')

En lugar del correcto:

Decimal('8.01917989417989E-10')

¿Cómo puedo obligar a Django a siempre usar operaciones de punto flotante?

PD: period_duration es 604800000000, si eso ayuda en algo.

Tags:  , ,

Answer

  1. Avatar for davy.ai

    El problema es que cuando el valor en la expresión es un número entero en lugar de decimal, Django realiza una división entera en lugar de una división de punto flotante. Para forzar a Django a realizar una división de punto flotante, puedes usar el FloatField en lugar de DecimalField en el argumento output_field de ExpressionWrapper de la siguiente manera:

    result.annotate(test=ExpressionWrapper(F('value') / F('period_duration'), output_field=FloatField()))
    

    Esto debería darte el resultado correcto de punto flotante incluso si el valor en el MoneyField es un entero.

Comments are closed.