Skip to content

Select the compact pattern from the rounded value - #1301

Open
Sreekant13 wants to merge 1 commit into
python-babel:masterfrom
Sreekant13:fix/compact-rounding-carry
Open

Select the compact pattern from the rounded value#1301
Sreekant13 wants to merge 1 commit into
python-babel:masterfrom
Sreekant13:fix/compact-rounding-carry

Conversation

@Sreekant13

Copy link
Copy Markdown

format_compact_decimal picks the magnitude bucket from the unrounded number and only rounds afterwards, so a value that rounds up into the next magnitude keeps the smaller unit:

>>> from babel.numbers import format_compact_decimal
>>> format_compact_decimal(999999, locale='en_US', format_type='short')
'1000K'          # expected '1M'
>>> format_compact_decimal(999999999, locale='en_US', format_type='short')
'1000M'          # expected '1B'
>>> format_compact_decimal(999999, locale='en_US', format_type='long')
'1000 thousand'  # expected '1 million'

"1000K" is not a valid compact form, since the mantissa is supposed to stay below the next magnitude. The CLDR algorithm that _get_compact_format links in its own docstring selects the pattern from the rounded value, so this redoes the lookup one magnitude up when rounding carries over.

The retry goes through _get_compact_format again rather than patching the pattern in place, so the next bucket's plural form is re-selected too; that is what makes the long format read "1 million" instead of "1 millions". It terminates because the bumped value sits exactly on the magnitude boundary and cannot carry a second time.

Values that do not carry over are unaffected: 999 -> 999, 1000 -> 1K, 99999 -> 100K, 9999999 -> 10M, 999949 with fraction_digits=1 -> 999.9K, 12345 with fraction_digits=2 -> 12.34K. Negatives keep their sign (-999999 -> -1M).

Added test_compact_rounding_carries_to_next_magnitude; it fails on the current code with assert '1000K' == '1M'.

The number test files (143 tests), the babel/numbers.py doctests (40) and ruff all pass locally. Note that tests/test_dates.py fails heavily in my environment with LookupError: Unknown timezone because tzdata is unavailable; those failures are identical with this change reverted and are unrelated to it.

format_compact_decimal chose the magnitude from the unrounded number and
only rounded afterwards, so a value that rounds up into the next magnitude
kept the smaller unit: 999999 rendered as "1000K" instead of "1M", and
likewise "1000M" for 999999999 and "1000 thousand" in the long format.

The CLDR algorithm that _get_compact_format documents selects the pattern
from the rounded value, so redo the lookup one magnitude up when rounding
carries over. Values that do not carry over are unaffected.
@akx

akx commented Jul 30, 2026

Copy link
Copy Markdown
Member

Note that tests/test_dates.py fails heavily in my environment with LookupError: Unknown timezone because tzdata is unavailable; those failures are identical with this change reverted and are unrelated to it.

You should fix your environment. Unless, of course, this comment was maybe written by an agent in a sandbox.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants