#32555 closed Bug (duplicate)
DecimalField Rounding inconsistency (PostgreSQL)
Reported by: | bluppfisk | Owned by: | nobody |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 2.2 |
Severity: | Normal | Keywords: | decimalfield, rounding, float |
Cc: | bluppfisk | Triage Stage: | Unreviewed |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
Note, I've initially posted this on 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.879'), 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.
Change History (3)
comment:1 by , 4 years ago
Resolution: | → duplicate |
---|---|
Status: | new → closed |
comment:2 by , 4 years ago
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.
comment:3 by , 4 years ago
Cc: | added |
---|
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 discussion and #28164.