Opened 19 months ago

Last modified 19 months ago

#26459 new New feature

Allow providing DecimalField with a custom context

Reported by: yasondinalt Owned by: nobody
Component: Database layer (models, ORM) Version: 1.9
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

DecimalField has weird behavior in case of float values passed to field.
If decimal_places = 1
float 2.15 will be saved as 2.1 (!)
float 2.25 will be saved as 2.2 (!)
But if float value first converted to str:
float 2.15 will be saved as 0.2 (ok)
float 2.25 will be saved as 0.2 (!)
It's because of default decimal rounding ROUND_HALF_EVEN.
As I understand build-in round() use rounding similar to ROUND_HALF_UP.

I tried first cast float to str, then use ROUND_HALF_UP, so now:
float 2.15 will be saved as 0.2 (ok)
float 2.25 will be saved as 0.3 (ok)

Attachments (2)

0001-tests-format-float-to-Decimal-with-proper-rounding.patch (1.1 KB) - added by yasondinalt 19 months ago.
Tests
0001-DecimalField-rounding-first-cast-to-str-then-use-ROU.patch (1.5 KB) - added by yasondinalt 19 months ago.
Changes

Download all attachments as: .zip

Change History (7)

comment:1 Changed 19 months ago by yasondinalt

Needs tests: set

comment:2 Changed 19 months ago by Tim Graham

Could you give a snippet of the application code where you run into this issue?

comment:3 in reply to:  2 Changed 19 months ago by yasondinalt

Replying to timgraham:

Could you give a snippet of the application code where you run into this issue?

class Invoice(models.Model):
    # same as django-paypal use
    mc_gross = models.DecimalField(max_digits=64, decimal_places=2, default=0)
    
invoice.mc_gross = 2.215 # float
invoice.save()

In DB will be value 2.21 (or if 2.225 -> 2.22)

Last edited 19 months ago by yasondinalt (previous) (diff)

comment:4 Changed 19 months ago by Tim Graham

Needs tests: unset

I'm not sure whether or not to accept the ticket so I asked for advice on the django-developers mailing list.

p.s. You don't need to attach patches on the ticket when you also send a pull request.

comment:5 Changed 19 months ago by Tim Graham

Has patch: unset
Summary: DecimalField float roundingAllow providing DecimalField with a custom context
Triage Stage: UnreviewedAccepted
Type: BugNew feature

Accepting per Aymeric's proposal on the mailing list:

Currently DecimalField accepts max_digits and decimal_places options. I think it should accept a decimal context and delegate all operations to that context.

I suggest the following behavior:

  • start with the decimal context provided in a kwarg to DecimalField or, if there is None, the current context returned by getcontext().
  • modify that context to take into account max_digits and decimal_places
  • ask the context to perform whatever operations we need
Note: See TracTickets for help on using tickets.
Back to Top