Fixed #5748 -- Made floatformat filter round properly on all platforms and handle NaN input correctly on Windows. Also added tests for these cases. Thanks for the report and initial patch to SmileyChris and PJCrosier.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@9369 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Karen Tracey 2008-11-08 01:44:46 +00:00
parent cadbf66635
commit b81bc22ad2
2 changed files with 63 additions and 15 deletions

View File

@ -1,6 +1,12 @@
"""Default variable filters."""
import re
try:
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
except ImportError:
from django.utils._decimal import Decimal, InvalidOperation, ROUND_HALF_UP
import random as random_module
try:
from functools import wraps
@ -45,7 +51,6 @@ def stringfilter(func):
# STRINGS #
###################
def addslashes(value):
"""
Adds slashes before quotes. Useful for escaping strings in CSV, for
@ -92,6 +97,18 @@ def fix_ampersands(value):
fix_ampersands.is_safe=True
fix_ampersands = stringfilter(fix_ampersands)
# Values for testing floatformat input against infinity and NaN representations,
# which differ across platforms and Python versions. Some (i.e. old Windows
# ones) are not recognized by Decimal but we want to return them unchanged vs.
# returning an empty string as we do for completley invalid input. Note these
# need to be built up from values that are not inf/nan, since inf/nan values do
# not reload properly from .pyc files on Windows prior to some level of Python 2.5
# (see Python Issue757815 and Issue1080440).
pos_inf = 1e200 * 1e200
neg_inf = -1e200 * 1e200
nan = (1e200 * 1e200) / (1e200 * 1e200)
special_floats = [str(pos_inf), str(neg_inf), str(nan)]
def floatformat(text, arg=-1):
"""
Displays a float to a specified number of decimal places.
@ -119,24 +136,42 @@ def floatformat(text, arg=-1):
* {{ num1|floatformat:"-3" }} displays "34.232"
* {{ num2|floatformat:"-3" }} displays "34"
* {{ num3|floatformat:"-3" }} displays "34.260"
If the input float is infinity or NaN, the (platform-dependent) string
representation of that value will be displayed.
"""
try:
f = float(text)
except (ValueError, TypeError):
input_val = force_unicode(text)
d = Decimal(input_val)
except UnicodeEncodeError:
return u''
except InvalidOperation:
if input_val in special_floats:
return input_val
else:
return u''
try:
d = int(arg)
p = int(arg)
except ValueError:
return force_unicode(f)
return input_val
try:
m = f - int(f)
except OverflowError:
return force_unicode(f)
if not m and d < 0:
return mark_safe(u'%d' % int(f))
m = int(d) - d
except (OverflowError, InvalidOperation):
return input_val
if not m and p < 0:
return mark_safe(u'%d' % (int(d)))
if p == 0:
exp = Decimal(1)
else:
formatstr = u'%%.%df' % abs(d)
return mark_safe(formatstr % f)
exp = Decimal('1.0') / (Decimal(10) ** abs(p))
try:
return mark_safe(u'%s' % str(d.quantize(exp, ROUND_HALF_UP)))
except InvalidOperation:
return input_val
floatformat.is_safe = True
def iriencode(value):

View File

@ -13,15 +13,15 @@ u'0.1'
u'0.0'
>>> floatformat(0.0)
u'0'
>>> floatformat(7.7,3)
>>> floatformat(7.7, 3)
u'7.700'
>>> floatformat(6.000000,3)
>>> floatformat(6.000000, 3)
u'6.000'
>>> floatformat(6.200000, 3)
u'6.200'
>>> floatformat(6.200000, -3)
u'6.200'
>>> floatformat(13.1031,-3)
>>> floatformat(13.1031, -3)
u'13.103'
>>> floatformat(11.1197, -2)
u'11.12'
@ -35,10 +35,23 @@ u'8.280'
u''
>>> floatformat(13.1031, u'bar')
u'13.1031'
>>> floatformat(18.125, 2)
u'18.13'
>>> floatformat(u'foo', u'bar')
u''
>>> floatformat(u'¿Cómo esta usted?')
u''
>>> floatformat(None)
u''
>>> pos_inf = float(1e30000)
>>> floatformat(pos_inf) == unicode(pos_inf)
True
>>> neg_inf = float(-1e30000)
>>> floatformat(neg_inf) == unicode(neg_inf)
True
>>> nan = pos_inf / pos_inf
>>> floatformat(nan) == unicode(nan)
True
>>> addslashes(u'"double quotes" and \'single quotes\'')
u'\\"double quotes\\" and \\\'single quotes\\\''