Skip to content

Commit b661c54

Browse files
author
Vinayak Deshmuk
committed
Address review: float inputs in the gap render from shortest representation; pin 10**36 boundary
- itzzdev09 noted intword(1e50) printed int()'s binary expansion in the new plain-number fallback. Format float inputs from Decimal(repr(abs(value))) instead, giving the clean digits the caller has in mind. Note: format(v, '.0f') would NOT fix this - float fixed-point formatting renders the exact binary value too. - Add test_intword_decillion_boundary pinning 10**36 -> plain number (intended change from '1000.0 decillion') and test_intword_gap_float_inputs. - Docstring: document the float behavior + doctest for intword(1e50).
1 parent ba2089e commit b661c54

2 files changed

Lines changed: 37 additions & 0 deletions

File tree

‎src/humanize/number.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
__lazy_modules__ = {"bisect"}
66

77
import bisect
8+
import decimal
89

910
from .i18n import _gettext as _
1011
from .i18n import _gettext_noop as N_
@@ -228,6 +229,9 @@ def intword(value: NumberOrString, format: str = "%.1f") -> str:
228229
up to decillion (33 digits) and googol (100 digits). There is no named unit
229230
between decillion and googol: values of 1000 decillion or more are returned as
230231
the plain number, except values rounding up to a googol, which become "1.0 googol".
232+
Float inputs in that range are rendered from their shortest representation, so
233+
`intword(1e50)` gives the clean 51-digit number rather than the float's binary
234+
expansion.
231235
232236
Examples:
233237
```pycon
@@ -241,6 +245,8 @@ def intword(value: NumberOrString, format: str = "%.1f") -> str:
241245
'1.2 billion'
242246
>>> intword(8100000000000000000000000000000000)
243247
'8.1 decillion'
248+
>>> intword(1e50)
249+
'100000000000000000000000000000000000000000000000000'
244250
>>> intword(None)
245251
'None'
246252
>>> intword("1234000", "%0.3f")
@@ -262,6 +268,13 @@ def intword(value: NumberOrString, format: str = "%.1f") -> str:
262268
try:
263269
if not math.isfinite(float(value)):
264270
return _format_not_finite(float(value))
271+
if isinstance(value, float):
272+
# The plain-number fallback below must render from the original
273+
# float's shortest representation: int(value) exposes the float's
274+
# binary expansion (int(1e50) != 10**50).
275+
plain_value: str | None = f"{decimal.Decimal(repr(abs(value))):f}"
276+
else:
277+
plain_value = None
265278
value = int(value)
266279
except (TypeError, ValueError):
267280
return str(value)
@@ -303,6 +316,8 @@ def intword(value: NumberOrString, format: str = "%.1f") -> str:
303316
ordinal += 1
304317
rounded_value = 1.0
305318
elif rounded_value >= 1000:
319+
if plain_value is not None:
320+
return f"{negative_prefix}{plain_value}"
306321
return f"{negative_prefix}{value}"
307322

308323
singular, plural = human_powers[ordinal]

‎tests/test_number.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,28 @@ def test_intword_decillion_googol_gap() -> None:
219219
assert humanize.intword(2 * 10**100) == "2.0 googol"
220220

221221

222+
def test_intword_decillion_boundary() -> None:
223+
"""Pin the 10**36 boundary explicitly.
224+
225+
1000 decillion has no named unit to carry into, so it renders as the plain
226+
number (changed from "1000.0 decillion" by the #356 fix), while values just
227+
below the boundary are untouched.
228+
"""
229+
assert humanize.intword(10**33) == "1.0 decillion"
230+
assert humanize.intword(9995 * 10**32) == "999.5 decillion"
231+
assert humanize.intword(10**36) == "1" + "0" * 36
232+
assert humanize.intword(-(10**36)) == "-1" + "0" * 36
233+
234+
235+
def test_intword_gap_float_inputs() -> None:
236+
"""Float inputs in the decillion..googol gap render from their shortest
237+
representation, not the float's binary expansion (review on #415)."""
238+
assert humanize.intword(1e50) == "1" + "0" * 50
239+
assert humanize.intword(-1e50) == "-1" + "0" * 50
240+
assert humanize.intword(1.5e50) == "15" + "0" * 49
241+
assert humanize.intword(1e36) == "1" + "0" * 36
242+
243+
222244
@pytest.mark.parametrize(
223245
"test_input, expected",
224246
[

0 commit comments

Comments
 (0)