Code

Opened 10 months ago

Last modified 10 months ago

#20648 new Bug

Template variable and loss of precision

Reported by: maciag.artur@… Owned by: nobody
Component: Template system Version: master
Severity: Normal Keywords:
Cc: bmispelon@… Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I use BigIntegerField in model. When i try use it as template variable I get different value. It's becouse all values are initially resolved as float (django/template/base.py:700) and then back to int if float not detected. Based on IEEE 754 it will work only for any integer with absolute value less than or equal to 253:

from django.template import Variable

Variable(str(2**53)).resolve(None) == 2**53

but not for greater than 253:

Variable(str(2**53+1)).resolve(None) == 2**53+1

Attachments (0)

Change History (3)

comment:1 Changed 10 months ago by bmispelon

  • Cc bmispelon@… added
  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Triage Stage changed from Unreviewed to Accepted
  • Version set to master

Hi,

I agree that the issue exist, but how much of a problem is this in practice?
Can you describe a concrete case where you ran into this problem.

From what I can tell, it'd be fairly easy to fix this: replace int(self.literal) by int(var) in Variable.__init__ [1]

I wonder however if there'd be performance costs to this.

I'm accepting this on the basis that this behavior should at least be documented, if not fixed.

Thanks.

[1] https://github.com/django/django/blob/master/django/template/base.py#L705

comment:2 Changed 10 months ago by maciag.artur@…

Like i said I use BigIntegerField and use its values in custom template tag which is combination of few smaller tags. All internal tags are rendered programmatically. I pass all args as Variable object to be able resolve them.

It's not big problem for me. I bypass the issue by overriding Variable.literal after making init.

comment:3 Changed 10 months ago by bmispelon

Note that the issue is only present for number literals. Something like Variable('foo').resolve({'foo': 2**53+1}) incurs not precision loss.

I'm not sure what you mean by "rendering programmatically" but I think you might be better off by not converting your variables to literals (maybe you could construct names dynamically and resolve on those?).

I made some quick benchmarks with timeit and it seems that converting an int from a str is 50% slower than from a float:

$ python -m timeit -s "x=float(2**53)" "int(x)"
1000000 loops, best of 3: 0.211 usec per loop
$ python -m timeit -s "x=str(2**53)" "int(x)"
1000000 loops, best of 3: 0.328 usec per loop

This would need to be confirmed in a more realistic scenario, but if it holds, it seems like a high price to pay for a corner case like this.
Maybe there's a more efficient way to go about this though (and if not, this limitation should be documented).

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as new
The owner will be changed from nobody to anonymous. Next status will be 'assigned'
as The resolution will be set. Next status will be 'closed'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.