Skip to content

Fix intword() rounding carry for values >= 10^24 - #401

Closed
Sreekant13 wants to merge 1 commit into
python-humanize:mainfrom
Sreekant13:fix-intword-carry-at-scale
Closed

Sreekant13 wants to merge 1 commit into
python-humanize:mainfrom
Sreekant13:fix-intword-carry-at-scale

Conversation

@Sreekant13

Copy link
Copy Markdown

Fixes #400.

intword()'s rounding carry (so 999_999 reads as "1.0 million", not "1000.0 thousand") silently stopped firing from septillion (10^24) upward:

>>> intword(10**24 - 1)   # was '1000.0 sextillion'
'1.0 septillion'
>>> intword(10**33 - 1)   # was '1000.0 nonillion'
'1.0 decillion'

Cause

The carry compared a float product against an exact int power:

if not largest_ordinal and rounded_value * power == powers[ordinal + 1]:

powers holds Python ints, and float(10**k) != 10**k once k >= 24, so 1000.0 * 10**21 == 10**24 is False and the carry was skipped. Below 10**24 the intermediate is float-exact, so it worked, which is why it went unnoticed.

Fix

Compare the rounded mantissa against the integer ratio between adjacent powers:

if not largest_ordinal and rounded_value >= powers[ordinal + 1] // power:

Every entry in powers is a power of ten, so the ratio is exact. It stays large (10**67) across the decillion-to-googol gap, so 10**36 is still "1000.0 decillion" and the #356 behaviour is unchanged.

Tests

Added 10**24 - 1 ... 10**33 - 1 to the test_intword table (all four fail on main, showing "1000.0 <smaller unit>"), plus the existing 10**36 -> "1000.0 decillion" case kept right after them as the control that the gap is untouched. Full test_number.py passes (236), and the existing small-number carries (999_999 -> "1.0 million", etc.) are unaffected.

The rounding carry compared `rounded_value * power == powers[ordinal + 1]`,
a float product against an exact int power. `float(10**k) != 10**k` for
k >= 24, so from septillion up the equality was False and the carry was
skipped: `10**24 - 1` rendered as "1000.0 sextillion" instead of
"1.0 septillion". Compare the rounded mantissa against the exact integer
ratio between adjacent powers instead; every power is a power of ten so the
ratio is exact, and it stays large across the decillion-to-googol gap, so
`10**36` is unchanged at "1000.0 decillion".

Fixes python-humanize#400.
@Sreekant13
Sreekant13 force-pushed the fix-intword-carry-at-scale branch from 58bd267 to 4a8dbb8 Compare September 16, 2026 03:22
@Sreekant13

Copy link
Copy Markdown
Author

Closing as a duplicate of #346, which fixes this same intword() carry bug and predates this PR by two months. Apologies for the dup: I missed #346 in my search before opening.

For what it is worth as an independent confirmation for reviewers: I reproduced the exact same boundary (the carry fails from 10^24 through 10^33, works below because float(10**k) is exact for k < 24), and #346's fix (rounded_value == powers[ordinal + 1] // power) plus its controls for the 10^36 decillion-to-googol gap and the googol case all hold. #346 looks correct and complete to me.

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.

intword() drops the rounding carry for values >= 10^24 (renders '1000.0 sextillion' instead of '1.0 septillion')

1 participant