# HG changeset patch # User fschwarz # Date 1343982981 0 # Node ID ea413a4d754bbd3f20beafa38997358625d8a5a3 # Parent 99d51589c822e00d83e977e7fcba5e92b104fdab fix format_decimal() with small Decimal values (#214, patch from George Lund) diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -42,6 +42,7 @@ * fix formatting of fraction in format_decimal() if the input value is a float with more than 7 significant digits (#183) * fix format_date() with datetime parameter (#282, patch from Xavier Morel) + * fix format_decimal() with small Decimal values (#214, patch from George Lund) Version 0.9.6 diff --git a/babel/numbers.py b/babel/numbers.py --- a/babel/numbers.py +++ b/babel/numbers.py @@ -323,9 +323,39 @@ def split_number(value): """Convert a number into a (intasstring, fractionasstring) tuple""" if isinstance(value, Decimal): - text = str(value) - else: - text = ('%.9f' % value).rstrip('0') + # NB can't just do text = str(value) as str repr of Decimal may be + # in scientific notation, e.g. for small numbers. + + sign, digits, exp = value.as_tuple() + # build list of digits in reverse order, then reverse+join + # as per http://docs.python.org/library/decimal.html#recipes + int_part = [] + frac_part = [] + + digits = map(str, digits) + + # get figures after decimal point + for i in range(-exp): + # add digit if available, else 0 + frac_part.append(digits.pop() if digits else '0') + + # add in some zeroes... + for i in range(exp): + int_part.append('0') + + # and the rest + while digits: + int_part.append(digits.pop()) + + # if < 1, int_part must be set to '0' + if len(int_part) == 0: + int_part = '0', + + if sign: + int_part.append('-') + + return ''.join(reversed(int_part)), ''.join(reversed(frac_part)) + text = ('%.9f' % value).rstrip('0') if '.' in text: a, b = text.split('.', 1) if b == '0': diff --git a/babel/tests/numbers.py b/babel/tests/numbers.py --- a/babel/tests/numbers.py +++ b/babel/tests/numbers.py @@ -140,6 +140,13 @@ # 0 (see ticket #99) fmt = numbers.format_scientific(0, '#E0', locale='en_US') self.assertEqual(fmt, '0E0') + + def test_formatting_of_very_small_decimals(self): + # previously formatting very small decimals could lead to a type error + # because the Decimal->string conversion was too simple (see #214) + number = Decimal("7E-7") + fmt = numbers.format_decimal(number, format="@@@", locale='en_US') + self.assertEqual('0.000000700', fmt) class BankersRoundTestCase(unittest.TestCase):