Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #5748 -- Made floatformat filter round properly on all platform…

…s 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
  • Loading branch information...
commit b81bc22ad241d2ff72344885a44eb61f0cf9a99c 1 parent cadbf66
Karen Tracey authored November 08, 2008
59  django/template/defaultfilters.py
... ...
@@ -1,6 +1,12 @@
1 1
 """Default variable filters."""
2 2
 
3 3
 import re
  4
+
  5
+try:
  6
+    from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
  7
+except ImportError:
  8
+    from django.utils._decimal import Decimal, InvalidOperation, ROUND_HALF_UP
  9
+
4 10
 import random as random_module
5 11
 try:
6 12
     from functools import wraps
@@ -45,7 +51,6 @@ def _dec(*args, **kwargs):
45 51
 # STRINGS         #
46 52
 ###################
47 53
 
48  
-
49 54
 def addslashes(value):
50 55
     """
51 56
     Adds slashes before quotes. Useful for escaping strings in CSV, for
@@ -92,6 +97,18 @@ def fix_ampersands(value):
92 97
 fix_ampersands.is_safe=True
93 98
 fix_ampersands = stringfilter(fix_ampersands)
94 99
 
  100
+# Values for testing floatformat input against infinity and NaN representations,
  101
+# which differ across platforms and Python versions.  Some (i.e. old Windows
  102
+# ones) are not recognized by Decimal but we want to return them unchanged vs. 
  103
+# returning an empty string as we do for completley invalid input.  Note these
  104
+# need to be built up from values that are not inf/nan, since inf/nan values do 
  105
+# not reload properly from .pyc files on Windows prior to some level of Python 2.5 
  106
+# (see Python Issue757815 and Issue1080440).
  107
+pos_inf = 1e200 * 1e200
  108
+neg_inf = -1e200 * 1e200
  109
+nan = (1e200 * 1e200) / (1e200 * 1e200)
  110
+special_floats = [str(pos_inf), str(neg_inf), str(nan)]
  111
+
95 112
 def floatformat(text, arg=-1):
96 113
     """
97 114
     Displays a float to a specified number of decimal places.
@@ -119,24 +136,42 @@ def floatformat(text, arg=-1):
119 136
     * {{ num1|floatformat:"-3" }} displays "34.232"
120 137
     * {{ num2|floatformat:"-3" }} displays "34"
121 138
     * {{ num3|floatformat:"-3" }} displays "34.260"
  139
+    
  140
+    If the input float is infinity or NaN, the (platform-dependent) string
  141
+    representation of that value will be displayed.
122 142
     """
  143
+        
123 144
     try:
124  
-        f = float(text)
125  
-    except (ValueError, TypeError):
  145
+        input_val = force_unicode(text)
  146
+        d = Decimal(input_val)
  147
+    except UnicodeEncodeError:
126 148
         return u''
  149
+    except InvalidOperation:
  150
+        if input_val in special_floats:
  151
+            return input_val
  152
+        else:
  153
+            return u''
127 154
     try:
128  
-        d = int(arg)
  155
+        p = int(arg)
129 156
     except ValueError:
130  
-        return force_unicode(f)
  157
+        return input_val
  158
+    
131 159
     try:
132  
-        m = f - int(f)
133  
-    except OverflowError:
134  
-        return force_unicode(f)
135  
-    if not m and d < 0:
136  
-        return mark_safe(u'%d' % int(f))
  160
+        m = int(d) - d
  161
+    except (OverflowError, InvalidOperation):
  162
+        return input_val
  163
+    
  164
+    if not m and p < 0:
  165
+        return mark_safe(u'%d' % (int(d)))
  166
+    
  167
+    if p == 0:
  168
+        exp = Decimal(1)
137 169
     else:
138  
-        formatstr = u'%%.%df' % abs(d)
139  
-        return mark_safe(formatstr % f)
  170
+        exp = Decimal('1.0') / (Decimal(10) ** abs(p))
  171
+    try:
  172
+        return mark_safe(u'%s' % str(d.quantize(exp, ROUND_HALF_UP)))
  173
+    except InvalidOperation:
  174
+        return input_val
140 175
 floatformat.is_safe = True
141 176
 
142 177
 def iriencode(value):
19  tests/regressiontests/defaultfilters/tests.py
@@ -13,15 +13,15 @@
13 13
 u'0.0'
14 14
 >>> floatformat(0.0)
15 15
 u'0'
16  
->>> floatformat(7.7,3)
  16
+>>> floatformat(7.7, 3)
17 17
 u'7.700'
18  
->>> floatformat(6.000000,3)
  18
+>>> floatformat(6.000000, 3)
19 19
 u'6.000'
20 20
 >>> floatformat(6.200000, 3)
21 21
 u'6.200'
22 22
 >>> floatformat(6.200000, -3)
23 23
 u'6.200'
24  
->>> floatformat(13.1031,-3)
  24
+>>> floatformat(13.1031, -3)
25 25
 u'13.103'
26 26
 >>> floatformat(11.1197, -2)
27 27
 u'11.12'
@@ -35,10 +35,23 @@
35 35
 u''
36 36
 >>> floatformat(13.1031, u'bar')
37 37
 u'13.1031'
  38
+>>> floatformat(18.125, 2) 
  39
+u'18.13' 
38 40
 >>> floatformat(u'foo', u'bar')
39 41
 u''
  42
+>>> floatformat(u'¿Cómo esta usted?')
  43
+u''
40 44
 >>> floatformat(None)
41 45
 u''
  46
+>>> pos_inf = float(1e30000)
  47
+>>> floatformat(pos_inf) == unicode(pos_inf)
  48
+True
  49
+>>> neg_inf = float(-1e30000)
  50
+>>> floatformat(neg_inf) == unicode(neg_inf)
  51
+True
  52
+>>> nan = pos_inf / pos_inf
  53
+>>> floatformat(nan) == unicode(nan)
  54
+True
42 55
 
43 56
 >>> addslashes(u'"double quotes" and \'single quotes\'')
44 57
 u'\\"double quotes\\" and \\\'single quotes\\\''

0 notes on commit b81bc22

Please sign in to comment.
Something went wrong with that request. Please try again.