Opened 11 years ago

Closed 5 years ago

#20648 closed Bug (duplicate)

Template variable and loss of precision

Reported by: maciag.artur@… Owned by: nobody
Component: Template system Version: dev
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

Change History (5)

comment:1 by Baptiste Mispelon, 11 years ago

Cc: bmispelon@… added
Triage Stage: UnreviewedAccepted
Version: 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 by maciag.artur@…, 11 years ago

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 by Baptiste Mispelon, 11 years ago

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).

comment:4 by Min ho Kim, 5 years ago

This issue seemed to be resolved already (at least master and 2.2.4).
I don't see any difference from the initial example code.

>>> from django.template import Variable
>>> Variable(str(2**53)).resolve(None) == 2**53
True
>>> Variable(str(2**53+1)).resolve(None) == 2**53+1
True

comment:5 by Mariusz Felisiak, 5 years ago

Resolution: duplicate
Status: newclosed
Note: See TracTickets for help on using tickets.
Back to Top