[Django] #32555: DecimalField Rounding inconsistency (PostgreSQL)

12 views
Skip to first unread message

Django

unread,
Mar 16, 2021, 6:39:29 AM3/16/21
to django-...@googlegroups.com
#32555: DecimalField Rounding inconsistency (PostgreSQL)
-------------------------------------+-------------------------------------
Reporter: bluppfisk | Owner: nobody
Type: Bug | Status: new
Component: Database | Version: 2.2
layer (models, ORM) | Keywords: decimalfield,
Severity: Normal | rounding, float
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
Note, I've initially posted this on
[https://stackoverflow.com/questions/66644473/django-decimalfield-
inconsistent-rounding stackoverflow]

Django 2.2, PostgreSQL database. I have a `Line` object with a `positions`
property. This is an `ArrayField` of a `DecimalField` with a `max_digits`
of 12 and `decimal_places` of 3.

I store some floats as `Decimal`s and get them back out of the Database:

{{{
pos = [6586.87849502, 2.04190477e-01, 7.14666669e-01]
line = Line(positions=pos)
line.save()
line.refresh_from_db() # send it through Django's ORM piping
print(line.positions)
}}}
Output:

> [Decimal('6586.87**9**'), Decimal('0.204'), Decimal('0.715')]

Interestingly, the first position was rounded up despite its next more
significant digit being below 5. The other float is rounded down as
expected.

I thought it might be PostgreSQL messing around, but no:

{{{
INSERT INTO line(positions) VALUES (array[6586.87849502, 2.04190477e-01,
7.14666669e-01]) RETURNING positions;
}}}

> positions (numeric[])
> {6586.878,0.204,0.715}


The real issue is that I should be able to predict what will come out of
the database, down to the required precision of three decimal places:

{{{
[Decimal(x).quantize(Decimal("0.001")) for x in pos]
}}}

might yield

> [Decimal('6586.878'), Decimal('0.204'), Decimal('0.715')]

or

> [Decimal('6586.879'), Decimal('0.205'), Decimal('0.714')]

depending on the `decimal.ROUND_*` flag I pass in `quantize()`, but I
never get consistent rounding throughout the array.

I also tried using the `django.db.backends.util::number_format` function
for every of my `Decimal`s as I gathered that this is used by Django
before inserting into the database, but the results are still
inconsistent.

--
Ticket URL: <https://code.djangoproject.com/ticket/32555>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Mar 16, 2021, 7:09:19 AM3/16/21
to django-...@googlegroups.com
#32555: DecimalField Rounding inconsistency (PostgreSQL)
-------------------------------------+-------------------------------------
Reporter: bluppfisk | Owner: nobody
Type: Bug | Status: closed
Component: Database layer | Version: 2.2
(models, ORM) |
Severity: Normal | Resolution: duplicate
Keywords: decimalfield, | Triage Stage:
rounding, float | Unreviewed
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Mariusz Felisiak):

* status: new => closed
* resolution: => duplicate


Comment:

I cannot reproduce the incosistent rounding, however I believe we can mark
this as a duplicate of #26459. Providing a custom context to
`DecimalField` should fix this and similar issues, see also
[https://groups.google.com/g/django-
developers/c/bnoVTOx2GFs/m/i0lNDpV8EgAJ discussion] and #28164.

--
Ticket URL: <https://code.djangoproject.com/ticket/32555#comment:1>

Django

unread,
Mar 16, 2021, 10:44:54 AM3/16/21
to django-...@googlegroups.com
#32555: DecimalField Rounding inconsistency (PostgreSQL)
-------------------------------------+-------------------------------------
Reporter: bluppfisk | Owner: nobody
Type: Bug | Status: closed
Component: Database layer | Version: 2.2
(models, ORM) |
Severity: Normal | Resolution: duplicate
Keywords: decimalfield, | Triage Stage:
rounding, float | Unreviewed
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by bluppfisk):

I agree that is related, but even as I imitate Django's behaviour by using
its own field and field configuration, the rounding is correct. It is only
when I leave it fully up to Django that the rounding error happens.

{{{
def _normalize_positions(self):
decimal_field = self._meta.get_field('positions').base_field
self.positions = [
decimal_field.context.create_decimal_from_float(val)
.quantize(Decimal("1").scaleb(-decimal_field.decimal_places),
context=decimal_field.context)
for val in self.positions
]
}}}

> [Decimal('6586.878'), Decimal('0.204'), Decimal('0.715')]

This is good.

What is Django doing differently than me? It would seem logical that
Django takes care of the normalising in a reproducible way.

--
Ticket URL: <https://code.djangoproject.com/ticket/32555#comment:2>

Django

unread,
Mar 16, 2021, 11:22:54 AM3/16/21
to django-...@googlegroups.com
#32555: DecimalField Rounding inconsistency (PostgreSQL)
-------------------------------------+-------------------------------------
Reporter: bluppfisk | Owner: nobody
Type: Bug | Status: closed
Component: Database layer | Version: 2.2
(models, ORM) |
Severity: Normal | Resolution: duplicate
Keywords: decimalfield, | Triage Stage:
rounding, float | Unreviewed
Has patch: 0 | Needs documentation: 0

Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by bluppfisk):

* cc: bluppfisk (added)


--
Ticket URL: <https://code.djangoproject.com/ticket/32555#comment:3>

Reply all
Reply to author
Forward
0 new messages