summaryrefslogtreecommitdiff
path: root/docs/numbers.rst
blob: df834eaf8c9615ec7154ba0887e715577ca77cdd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
.. -*- mode: rst; encoding: utf-8 -*-

.. _numbers:

=================
Number Formatting
=================


Support for locale-specific formatting and parsing of numbers is provided by
the ``babel.numbers`` module:

.. code-block:: pycon

    >>> from babel.numbers import format_number, format_decimal, format_percent

Examples:

.. code-block:: pycon

    >>> format_decimal(1.2345, locale='en_US')
    u'1.234'
    >>> format_decimal(1.2345, locale='sv_SE')
    u'1,234'
    >>> format_decimal(12345, locale='de_DE')
    u'12.345'


Pattern Syntax
==============

While Babel makes it simple to use the appropriate number format for a given
locale, you can also force it to use custom patterns. As with date/time
formatting patterns, the patterns Babel supports for number formatting are
based on the `Locale Data Markup Language specification`_ (LDML).

Examples:

.. code-block:: pycon

    >>> format_decimal(-1.2345, format='#,##0.##;-#', locale='en')
    u'-1.23'
    >>> format_decimal(-1.2345, format='#,##0.##;(#)', locale='en')
    u'(1.23)'

The syntax for custom number format patterns is described in detail in the
the specification. The following table is just a relatively brief overview.

 .. _`Locale Data Markup Language specification`:
    https://unicode.org/reports/tr35/#Number_Format_Patterns

  +----------+-----------------------------------------------------------------+
  | Symbol   | Description                                                     |
  +==========+=================================================================+
  | ``0``    | Digit                                                           |
  +----------+-----------------------------------------------------------------+
  | ``1-9``  | '1' through '9' indicate rounding.                              |
  +----------+-----------------------------------------------------------------+
  | ``@``    | Significant digit                                               |
  +----------+-----------------------------------------------------------------+
  | ``#``    | Digit, zero shows as absent                                     |
  +----------+-----------------------------------------------------------------+
  | ``.``    | Decimal separator or monetary decimal separator                 |
  +----------+-----------------------------------------------------------------+
  | ``-``    | Minus sign                                                      |
  +----------+-----------------------------------------------------------------+
  | ``,``    | Grouping separator                                              |
  +----------+-----------------------------------------------------------------+
  | ``E``    | Separates mantissa and exponent in scientific notation          |
  +----------+-----------------------------------------------------------------+
  | ``+``    | Prefix positive exponents with localized plus sign              |
  +----------+-----------------------------------------------------------------+
  | ``;``    | Separates positive and negative subpatterns                     |
  +----------+-----------------------------------------------------------------+
  | ``%``    | Multiply by 100 and show as percentage                          |
  +----------+-----------------------------------------------------------------+
  | ``‰``    | Multiply by 1000 and show as per mille                          |
  +----------+-----------------------------------------------------------------+
  | ``¤``    | Currency sign, replaced by currency symbol. If doubled,         |
  |          | replaced by international currency symbol. If tripled, uses the |
  |          | long form of the decimal symbol.                                |
  +----------+-----------------------------------------------------------------+
  | ``'``    | Used to quote special characters in a prefix or suffix          |
  +----------+-----------------------------------------------------------------+
  | ``*``    | Pad escape, precedes pad character                              |
  +----------+-----------------------------------------------------------------+


Rounding Modes
==============

Since Babel makes full use of Python's `Decimal`_ type to perform number
rounding before formatting, users have the chance to control the rounding mode
and other configurable parameters through the active `Context`_ instance.

By default, Python rounding mode is ``ROUND_HALF_EVEN`` which complies with
`UTS #35 section 3.3`_.  Yet, the caller has the opportunity to tweak the
current context before formatting a number or currency:

.. code-block:: pycon

    >>> from babel.numbers import decimal, format_decimal
    >>> with decimal.localcontext(decimal.Context(rounding=decimal.ROUND_DOWN)):
    >>>    txt = format_decimal(123.99, format='#', locale='en_US')
    >>> txt
    u'123'

It is also possible to use ``decimal.setcontext`` or directly modifying the
instance returned by ``decimal.getcontext``.  However, using a context manager
is always more convenient due to the automatic restoration and the ability to
nest them.

Whatever mechanism is chosen, always make use of the ``decimal`` module imported
from ``babel.numbers``.  For efficiency reasons, Babel uses the fastest decimal
implementation available, such as `cdecimal`_.  These various implementation
offer an identical API, but their types and instances do **not** interoperate
with each other.

For example, the previous example can be slightly modified to generate
unexpected results on Python 2.7, with the `cdecimal`_ module installed:

.. code-block:: pycon

    >>> from decimal import localcontext, Context, ROUND_DOWN
    >>> from babel.numbers import format_decimal
    >>> with localcontext(Context(rounding=ROUND_DOWN)):
    >>>    txt = format_decimal(123.99, format='#', locale='en_US')
    >>> txt
    u'124'

Changing other parameters such as the precision may also alter the results of
the number formatting functions.  Remember to test your code to make sure it
behaves as desired.

.. _Decimal: https://docs.python.org/3/library/decimal.html#decimal-objects
.. _Context: https://docs.python.org/3/library/decimal.html#context-objects
.. _`UTS #35 section 3.3`: https://www.unicode.org/reports/tr35/tr35-numbers.html#Formatting
.. _cdecimal: https://pypi.org/project/cdecimal/


Parsing Numbers
===============

Babel can also parse numeric data in a locale-sensitive manner:

.. code-block:: pycon

    >>> from babel.numbers import parse_decimal, parse_number

Examples:

.. code-block:: pycon

    >>> parse_decimal('1,099.98', locale='en_US')
    1099.98
    >>> parse_decimal('1.099,98', locale='de')
    1099.98
    >>> parse_decimal('2,109,998', locale='de')
    Traceback (most recent call last):
      ...
    NumberFormatError: '2,109,998' is not a valid decimal number

.. note:: Number parsing is not properly implemented yet