Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Fix for #14317 (number formatting issues) #348

Closed
wants to merge 1 commit into from

3 participants

rvarshney Florian Apolloner Berker Peksag
rvarshney

Fix based off the diff in https://code.djangoproject.com/ticket/14317
Please review @akaariai.

django/utils/numberformat.py
((6 lines not shown))
# Make the common case fast
if isinstance(number, int) and not use_grouping and not decimal_pos:
return mark_safe(six.text_type(number))
- # sign
- if float(number) < 0:
+
+ float_number = float(number)
+ str_number = six.text_type(number)
+
+ if decimal_pos is not None:
+ # Use the %f format string. This gives us rounding and the
+ # right number of decimal positions automatically. This also
+ # removes any 'e' if the number is really small or really large
+ str_number = six.text_type(('%.' + str(decimal_pos) + 'f') % (float_number))
% (float_number)

Note: The parentheses are not required here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Florian Apolloner
Owner

Updated version of the patch is on the ticket.

Florian Apolloner apollo13 closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 11, 2012
  1. Ruchi Varshney
This page is out of date. Refresh to see the latest.
Showing with 43 additions and 30 deletions.
  1. +30 −24 django/utils/numberformat.py
  2. +13 −6 tests/regressiontests/i18n/tests.py
54 django/utils/numberformat.py
View
@@ -3,8 +3,7 @@
from django.utils import six
-def format(number, decimal_sep, decimal_pos=None, grouping=0, thousand_sep='',
- force_grouping=False):
+def format(number, decimal_sep, decimal_pos=None, grouping=0, thousand_sep='', force_grouping=False):
"""
Gets a number (as a number or string), and returns it as a string,
using formats defined as arguments:
@@ -17,35 +16,42 @@ def format(number, decimal_sep, decimal_pos=None, grouping=0, thousand_sep='',
use_grouping = settings.USE_L10N and settings.USE_THOUSAND_SEPARATOR
use_grouping = use_grouping or force_grouping
use_grouping = use_grouping and grouping > 0
+ use_grouping = use_grouping and thousand_sep
+
# Make the common case fast
if isinstance(number, int) and not use_grouping and not decimal_pos:
return mark_safe(six.text_type(number))
- # sign
- if float(number) < 0:
+
+ float_number = float(number)
+ str_number = six.text_type(number)
+
+ if decimal_pos is not None:
+ # Use the %f format string. This gives us rounding and the
+ # right number of decimal positions automatically. This also
+ # removes any 'e' if the number is really small or really large
+ str_number = six.text_type(('%.' + str(decimal_pos) + 'f') % float_number)
+
+ if not use_grouping:
+ return str_number.replace('.', decimal_sep)
+
+ # For grouping, we need to separate the sign, int_part and decimal part
+ if float_number < 0:
sign = '-'
+ str_number = str_number[1:]
else:
sign = ''
- str_number = six.text_type(number)
- if str_number[0] == '-':
- str_number = str_number[1:]
- # decimal part
- if '.' in str_number:
+
+ if decimal_pos is not None or '.' in str_number:
int_part, dec_part = str_number.split('.')
- if decimal_pos is not None:
- dec_part = dec_part[:decimal_pos]
+ dec_part = decimal_sep + dec_part
else:
int_part, dec_part = str_number, ''
- if decimal_pos is not None:
- dec_part = dec_part + ('0' * (decimal_pos - len(dec_part)))
- if dec_part:
- dec_part = decimal_sep + dec_part
- # grouping
- if use_grouping:
- int_part_gd = ''
- for cnt, digit in enumerate(int_part[::-1]):
- if cnt and not cnt % grouping:
- int_part_gd += thousand_sep
- int_part_gd += digit
- int_part = int_part_gd[::-1]
- return sign + int_part + dec_part
+ first_part_len = len(int_part) % grouping
+ full_parts = int_part[first_part_len:]
+ groups = [full_parts[i:i+grouping] for i in range(0, len(full_parts), grouping)]
+
+ if first_part_len > 0:
+ groups.insert(0, int_part[0:first_part_len])
+
+ return sign + thousand_sep.join(groups) + dec_part
19 tests/regressiontests/i18n/tests.py
View
@@ -334,14 +334,21 @@ def test_locale_independent(self):
Localization of numbers
"""
with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=False):
- self.assertEqual('66666.66', nformat(self.n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=','))
- self.assertEqual('66666A6', nformat(self.n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B'))
- self.assertEqual('66666', nformat(self.n, decimal_sep='X', decimal_pos=0, grouping=1, thousand_sep='Y'))
+ self.assertEqual(u'66666.67', nformat(self.n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=','))
+ self.assertEqual(u'66666A7', nformat(self.n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B'))
+ self.assertEqual('66667', nformat(self.n, decimal_sep='X', decimal_pos=0, grouping=1, thousand_sep='Y'))
+
+ # ticket #14317
+ self.assertEqual(u'0,00', nformat(0.000000001, decimal_sep=',', decimal_pos=2))
+ self.assertEqual(u'0,00', nformat(0.00000000000099, decimal_sep=',', decimal_pos=2))
+ self.assertEqual(u'0,00', nformat(decimal.Decimal(1e-37), decimal_sep=',', decimal_pos=2))
+ self.assertEqual(u'0,67', nformat(0.666, decimal_sep=',', decimal_pos=2))
+ self.assertEqual(u'0,67', nformat('0.666', decimal_sep=',', decimal_pos=2))
with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True):
- self.assertEqual('66,666.66', nformat(self.n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=','))
- self.assertEqual('6B6B6B6B6A6', nformat(self.n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B'))
- self.assertEqual('-66666.6', nformat(-66666.666, decimal_sep='.', decimal_pos=1))
+ self.assertEqual('66,666.67', nformat(self.n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=','))
+ self.assertEqual('6B6B6B6B6A7', nformat(self.n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B'))
+ self.assertEqual('-66666.7', nformat(-66666.666, decimal_sep='.', decimal_pos=1))
self.assertEqual('-66666.0', nformat(int('-66666'), decimal_sep='.', decimal_pos=1))
self.assertEqual('10000.0', nformat(self.l, decimal_sep='.', decimal_pos=1))
# This unusual grouping/force_grouping combination may be triggered by the intcomma filter (#17414)
Something went wrong with that request. Please try again.