# HG changeset patch # User jonas # Date 1184098445 0 # Node ID 1adcbeb4607b14d8c6439f9ac31bd0a21b9faa98 # Parent 6c8b69e150a982eba6f23c06d1815701dc64efe1 Added support for siginificant digits in number patterns. diff --git a/babel/numbers.py b/babel/numbers.py --- a/babel/numbers.py +++ b/babel/numbers.py @@ -249,7 +249,6 @@ # Filling # Rounding increment in pattern # Scientific notation -# Significant Digits def parse_pattern(pattern): """Parse number format patterns""" if isinstance(pattern, NumberPattern): @@ -264,6 +263,10 @@ pos_prefix, number, pos_suffix = number_re.search(pattern).groups() neg_prefix = '-' + pos_prefix neg_suffix = pos_suffix + if '@' in number: + if '.' in number and '0' in number: + raise ValueError('Significant digit patterns can not contain ' + '"@" or "0"') if '.' in number: integer, fraction = number.rsplit('.', 1) else: @@ -275,11 +278,13 @@ """Calculate the min and max allowed digits""" min = max = 0 for c in p: - if c == '0': + if c in '@0': min += 1 max += 1 elif c == '#': max += 1 + elif c == ',': + continue else: break return min, max @@ -308,7 +313,8 @@ int_precision = parse_precision(integer) frac_precision = parse_precision(fraction) grouping = parse_grouping(integer) - int_precision = (int_precision[0], 1000) # Unlimited + if not '@' in pattern: + int_precision = (int_precision[0], 1000) # Unlimited return NumberPattern(pattern, (pos_prefix, neg_prefix), (pos_suffix, neg_suffix), grouping, int_precision, frac_precision) @@ -338,18 +344,60 @@ def apply(self, value, locale, currency=None): value *= self.scale negative = int(value < 0) - a, b = (self.format % abs(value)).split('.', 1) - retval = u'%s%s%s%s' % (self.prefix[negative], - self._format_int(a, locale), - self._format_frac(b, locale), + if '@' in self.pattern: # Is it a siginificant digits pattern? + text = self._format_sigdig(abs(value), + self.int_precision[0], + self.int_precision[1]) + if '.' in text: + a, b = text.split('.') + a = self._format_int(a, 0, 1000, locale) + if b: + b = get_decimal_symbol(locale) + b + else: + a, b = self._format_int(text, 0, 1000, locale), '' + else: # A normal number pattern + a, b = (self.format % abs(value)).split('.', 1) + a = self._format_int(a, self.int_precision[0], + self.int_precision[1], locale) + b = self._format_frac(b, locale) + retval = u'%s%s%s%s' % (self.prefix[negative], a, b, self.suffix[negative]) if u'¤' in retval: retval = retval.replace(u'¤¤', currency.upper()) retval = retval.replace(u'¤', get_currency_symbol(currency, locale)) return retval - def _format_int(self, value, locale): - min, max = self.int_precision + def _format_sigdig(self, value, min, max): + """Convert value to a string. + + The resulting string will contain between (min, max) number of + significant digits. + """ + a, b = str(float(value)).split('.', 1) + ndecimals = len(a) + if a == '0': + ndecimals = 0 + while b.startswith('0'): + b = b[1:] + ndecimals -= 1 + text = str(round(value, max - ndecimals)) + if text == '0.0': + text = a = '0' + b = '' + digits = 1 + else: + a, b = text.split('.', 1) + digits = len(text.replace('.', '').lstrip('0')) + # Figure out if we need to add any trailing '0':s + if len(a) >= max and a != '0': + return a + if digits < min: + b += ('0' * (min - digits)) + if b: + return '%s.%s' % (a, b) + return a + + def _format_int(self, value, min, max, locale): width = len(value) if width < min: value += '0' * (min - width) diff --git a/babel/tests/numbers.py b/babel/tests/numbers.py --- a/babel/tests/numbers.py +++ b/babel/tests/numbers.py @@ -36,7 +36,42 @@ self.assertEqual(numbers.format_decimal(1.2325, locale='sv'), '1,232') self.assertEqual(numbers.format_decimal(1.2335, locale='sv'), '1,234') - + def test_significant_digits(self): + """Test significant digits patterns""" + self.assertEqual(numbers.format_decimal(123004, '@@',locale='en_US'), + '120000') + self.assertEqual(numbers.format_decimal(1.12, '@', locale='sv'), '1') + self.assertEqual(numbers.format_decimal(1.1, '@@', locale='sv'), '1,1') + self.assertEqual(numbers.format_decimal(1.1, '@@@@@##', locale='sv'), + '1,1000') + self.assertEqual(numbers.format_decimal(0.0001, '@@@', locale='sv'), + '0,000100') + self.assertEqual(numbers.format_decimal(0.0001234, '@@@', locale='sv'), + '0,000123') + self.assertEqual(numbers.format_decimal(0.0001234, '@@@#',locale='sv'), + '0,0001234') + self.assertEqual(numbers.format_decimal(0.0001234, '@@@#',locale='sv'), + '0,0001234') + self.assertEqual(numbers.format_decimal(0.12345, '@@@',locale='sv'), + '0,123') + self.assertEqual(numbers.format_decimal(3.14159, '@@##',locale='sv'), + '3,142') + self.assertEqual(numbers.format_decimal(1.23004, '@@##',locale='sv'), + '1,23') + self.assertEqual(numbers.format_decimal(1230.04, '@@,@@',locale='en_US'), + '12,30') + self.assertEqual(numbers.format_decimal(123.41, '@@##',locale='en_US'), + '123.4') + self.assertEqual(numbers.format_decimal(1, '@@',locale='en_US'), + '1.0') + self.assertEqual(numbers.format_decimal(0, '@',locale='en_US'), + '0') + self.assertEqual(numbers.format_decimal(0.1, '@',locale='en_US'), + '0.1') + self.assertEqual(numbers.format_decimal(0.1, '@#',locale='en_US'), + '0.1') + self.assertEqual(numbers.format_decimal(0.1, '@@',locale='en_US'), + '0.10') def suite(): suite = unittest.TestSuite() suite.addTest(doctest.DocTestSuite(numbers))