Skip to content

Commit c0d6e18

Browse files
mmaxjrclaude
andcommitted
fix(intword): correct decillion-to-googol gap and carry-over rounding
`powers` jumps from decillion (10**33) directly to googol (10**100), so any value in [10**36, 10**100) was chopped into a decillion count that grew arbitrarily large (e.g. 10**50 -> "100000000000000000.0 decillion"), and values that should round up into googol (e.g. 10**100 - 10**93) never carried over. Only advance to the next named unit when the value actually rounds up to it; otherwise there is no unit for that magnitude, so fall back to the plain integer, mirroring how intword() already falls back to a plain value below the smallest unit. Fixes #356 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 42b4a1d commit c0d6e18

2 files changed

Lines changed: 32 additions & 7 deletions

File tree

src/humanize/number.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,24 @@ def intword(value: NumberOrString, format: str = "%.1f") -> str:
255255
chopped = value / power
256256
rounded_value = float(format % chopped)
257257

258-
if not largest_ordinal and rounded_value * power == powers[ordinal + 1]:
259-
# After rounding, we end up just at the next power
260-
ordinal += 1
261-
rounded_value = 1.0
258+
if not largest_ordinal and rounded_value >= 1000:
259+
next_power = powers[ordinal + 1]
260+
if next_power == power * 1000:
261+
# After rounding, we end up just at the next power
262+
ordinal += 1
263+
rounded_value = 1.0
264+
else:
265+
# `powers` has a gap between this unit and the next one (e.g.
266+
# decillion to googol), so there is no unit for "1000+ of the
267+
# current one". Only bump to the next unit if the value is
268+
# close enough to actually round up to it; otherwise there is
269+
# no name for this magnitude, so fall back to the plain number.
270+
next_rounded_value = float(format % (value / next_power))
271+
if next_rounded_value >= 1.0:
272+
ordinal += 1
273+
rounded_value = next_rounded_value
274+
else:
275+
return f"{negative_prefix}{value}"
262276

263277
singular, plural = human_powers[ordinal]
264278
unit = _ngettext(singular, plural, math.ceil(rounded_value))

tests/test_number.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,21 @@ def test_intword_powers() -> None:
116116
(["3500000000000000000000"], "3.5 sextillion"),
117117
(["8100000000000000000000000000000000"], "8.1 decillion"),
118118
(["-8100000000000000000000000000000000"], "-8.1 decillion"),
119-
([1_000_000_000_000_000_000_000_000_000_000_000_000], "1000.0 decillion"),
120-
([1_100_000_000_000_000_000_000_000_000_000_000_000], "1100.0 decillion"),
121-
([2_100_000_000_000_000_000_000_000_000_000_000_000], "2100.0 decillion"),
119+
(
120+
[1_000_000_000_000_000_000_000_000_000_000_000_000],
121+
"1000000000000000000000000000000000000",
122+
),
123+
(
124+
[1_100_000_000_000_000_000_000_000_000_000_000_000],
125+
"1100000000000000000000000000000000000",
126+
),
127+
(
128+
[2_100_000_000_000_000_000_000_000_000_000_000_000],
129+
"2100000000000000000000000000000000000",
130+
),
122131
([2e100], "2.0 googol"),
132+
([10**50], str(10**50)),
133+
([10**100 - 10**93], "1.0 googol"),
123134
([None], "None"),
124135
(["1230000", "%0.2f"], "1.23 million"),
125136
([10**100], "1.0 googol"),

0 commit comments

Comments
 (0)