From d60cb04f9e18ec41c595e5800ecec911adfe1db5 Mon Sep 17 00:00:00 2001 From: Kyle Pereira Date: Sat, 24 Oct 2026 13:58:35 -0300 Subject: [PATCH 1/3] Extend intword support through vigintillion --- src/humanize/number.py | 14 ++++++++++++-- tests/test_number.py | 16 +++++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/humanize/number.py b/src/humanize/number.py index 52a5356a..9b5d56b4 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -203,7 +203,7 @@ def intcomma(value: NumberOrString, ndigits: int | None = None) -> str: return result -powers = [10**x for x in (3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 100)] +powers = [10**x for x in (3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 100)] human_powers = ( NS_("thousand", "thousand"), NS_("million", "million"), @@ -216,6 +216,16 @@ def intcomma(value: NumberOrString, ndigits: int | None = None) -> str: NS_("octillion", "octillion"), NS_("nonillion", "nonillion"), NS_("decillion", "decillion"), + NS_("undecillion", "undecillion"), + NS_("duodecillion", "duodecillion"), + NS_("tredecillion", "tredecillion"), + NS_("quattuordecillion", "quattuordecillion"), + NS_("quindecillion", "quindecillion"), + NS_("sexdecillion", "sexdecillion"), + NS_("septendecillion", "septendecillion"), + NS_("octodecillion", "octodecillion"), + NS_("novemdecillion", "novemdecillion"), + NS_("vigintillion", "vigintillion"), NS_("googol", "googol"), ) @@ -225,7 +235,7 @@ def intword(value: NumberOrString, format: str = "%.1f") -> str: Works best for numbers over 1 million. For example, 1_000_000 becomes "1.0 million", 1_200_000 becomes "1.2 million" and "1_200_000_000" becomes "1.2 billion". Supports - up to decillion (33 digits) and googol (100 digits). + up to vigintillion (63 digits) and googol (100 digits). Examples: ```pycon diff --git a/tests/test_number.py b/tests/test_number.py index 5fb12fa6..00fcfd15 100644 --- a/tests/test_number.py +++ b/tests/test_number.py @@ -123,9 +123,9 @@ def test_intword_powers() -> None: (["3500000000000000000000"], "3.5 sextillion"), (["8100000000000000000000000000000000"], "8.1 decillion"), (["-8100000000000000000000000000000000"], "-8.1 decillion"), - ([1_000_000_000_000_000_000_000_000_000_000_000_000], "1000.0 decillion"), - ([1_100_000_000_000_000_000_000_000_000_000_000_000], "1100.0 decillion"), - ([2_100_000_000_000_000_000_000_000_000_000_000_000], "2100.0 decillion"), + ([1_000_000_000_000_000_000_000_000_000_000_000_000], "1.0 undecillion"), + ([1_100_000_000_000_000_000_000_000_000_000_000_000], "1.1 undecillion"), + ([2_100_000_000_000_000_000_000_000_000_000_000_000], "2.1 undecillion"), ([2e100], "2.0 googol"), ([None], "None"), (["1230000", "%0.2f"], "1.23 million"), @@ -184,8 +184,8 @@ def test_intword_rounding_rollover() -> None: mantissa = float(rendered.split(" ", 1)[0]) assert mantissa < 1000 - # The documented decillion..googol gap must be left untouched. - assert humanize.intword(10**36) == "1000.0 decillion" + # Extended number names now cover undecillion through vigintillion. + assert humanize.intword(10**36) == "1.0 undecillion" assert humanize.intword(2 * 10**100) == "2.0 googol" @@ -377,3 +377,9 @@ def test_clamp(test_args: list[typing.Any], expected: str) -> None: ) def test_metric(test_args: list[typing.Any], expected: str) -> None: assert humanize.metric(*test_args) == expected + +def test_intword_extended_number_names() -> None: + assert humanize.intword(10**36) == "1.0 undecillion" + assert humanize.intword(10**39) == "1.0 duodecillion" + assert humanize.intword(-10**36) == "-1.0 undecillion" + assert humanize.intword(10**36, "%.2f") == "1.00 undecillion" \ No newline at end of file From 673e8873662e48120b471187a913a26bc7d32cd6 Mon Sep 17 00:00:00 2001 From: Kyle Pereira Date: Sat, 24 Oct 2026 14:37:12 -0300 Subject: [PATCH 2/3] Complete intword support through untrigintillion --- src/humanize/number.py | 15 +++++++++++++-- tests/test_number.py | 14 +++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/humanize/number.py b/src/humanize/number.py index 9b5d56b4..49c6e7b8 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -203,7 +203,7 @@ def intcomma(value: NumberOrString, ndigits: int | None = None) -> str: return result -powers = [10**x for x in (3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 100)] +powers = [10**x for x in (3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 100)] human_powers = ( NS_("thousand", "thousand"), NS_("million", "million"), @@ -226,6 +226,17 @@ def intcomma(value: NumberOrString, ndigits: int | None = None) -> str: NS_("octodecillion", "octodecillion"), NS_("novemdecillion", "novemdecillion"), NS_("vigintillion", "vigintillion"), + NS_("unvigintillion", "unvigintillion"), + NS_("duovigintillion", "duovigintillion"), + NS_("trevigintillion", "trevigintillion"), + NS_("quattuorvigintillion", "quattuorvigintillion"), + NS_("quinvigintillion", "quinvigintillion"), + NS_("sexvigintillion", "sexvigintillion"), + NS_("septenvigintillion", "septenvigintillion"), + NS_("octovigintillion", "octovigintillion"), + NS_("novemvigintillion", "novemvigintillion"), + NS_("trigintillion", "trigintillion"), + NS_("untrigintillion", "untrigintillion"), NS_("googol", "googol"), ) @@ -235,7 +246,7 @@ def intword(value: NumberOrString, format: str = "%.1f") -> str: Works best for numbers over 1 million. For example, 1_000_000 becomes "1.0 million", 1_200_000 becomes "1.2 million" and "1_200_000_000" becomes "1.2 billion". Supports - up to vigintillion (63 digits) and googol (100 digits). + up to untrigintillion (96 digits) and googol (100 digits). Examples: ```pycon diff --git a/tests/test_number.py b/tests/test_number.py index 00fcfd15..fe90a687 100644 --- a/tests/test_number.py +++ b/tests/test_number.py @@ -382,4 +382,16 @@ def test_intword_extended_number_names() -> None: assert humanize.intword(10**36) == "1.0 undecillion" assert humanize.intword(10**39) == "1.0 duodecillion" assert humanize.intword(-10**36) == "-1.0 undecillion" - assert humanize.intword(10**36, "%.2f") == "1.00 undecillion" \ No newline at end of file + assert humanize.intword(10**36, "%.2f") == "1.00 undecillion" + assert humanize.intword(10**66) == "1.0 unvigintillion" + assert humanize.intword(10**69) == "1.0 duovigintillion" + assert humanize.intword(10**72) == "1.0 trevigintillion" + assert humanize.intword(10**75) == "1.0 quattuorvigintillion" + assert humanize.intword(10**78) == "1.0 quinvigintillion" + assert humanize.intword(10**81) == "1.0 sexvigintillion" + assert humanize.intword(10**84) == "1.0 septenvigintillion" + assert humanize.intword(10**87) == "1.0 octovigintillion" + assert humanize.intword(10**90) == "1.0 novemvigintillion" + assert humanize.intword(10**93) == "1.0 trigintillion" + assert humanize.intword(10**96) == "1.0 untrigintillion" + assert humanize.intword(10**100) == "1.0 googol" \ No newline at end of file From feac4eb8a73856db41fc4622a33d8b75b7107523 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 24 Oct 2026 17:41:23 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/humanize/number.py | 39 ++++++++++++++++++++++++++++++++++++++- tests/test_number.py | 5 +++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/humanize/number.py b/src/humanize/number.py index 49c6e7b8..c3509a3d 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -203,7 +203,44 @@ def intcomma(value: NumberOrString, ndigits: int | None = None) -> str: return result -powers = [10**x for x in (3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 100)] +powers = [ + 10**x + for x in ( + 3, + 6, + 9, + 12, + 15, + 18, + 21, + 24, + 27, + 30, + 33, + 36, + 39, + 42, + 45, + 48, + 51, + 54, + 57, + 60, + 63, + 66, + 69, + 72, + 75, + 78, + 81, + 84, + 87, + 90, + 93, + 96, + 100, + ) +] human_powers = ( NS_("thousand", "thousand"), NS_("million", "million"), diff --git a/tests/test_number.py b/tests/test_number.py index fe90a687..9b6684d5 100644 --- a/tests/test_number.py +++ b/tests/test_number.py @@ -378,10 +378,11 @@ def test_clamp(test_args: list[typing.Any], expected: str) -> None: def test_metric(test_args: list[typing.Any], expected: str) -> None: assert humanize.metric(*test_args) == expected + def test_intword_extended_number_names() -> None: assert humanize.intword(10**36) == "1.0 undecillion" assert humanize.intword(10**39) == "1.0 duodecillion" - assert humanize.intword(-10**36) == "-1.0 undecillion" + assert humanize.intword(-(10**36)) == "-1.0 undecillion" assert humanize.intword(10**36, "%.2f") == "1.00 undecillion" assert humanize.intword(10**66) == "1.0 unvigintillion" assert humanize.intword(10**69) == "1.0 duovigintillion" @@ -394,4 +395,4 @@ def test_intword_extended_number_names() -> None: assert humanize.intword(10**90) == "1.0 novemvigintillion" assert humanize.intword(10**93) == "1.0 trigintillion" assert humanize.intword(10**96) == "1.0 untrigintillion" - assert humanize.intword(10**100) == "1.0 googol" \ No newline at end of file + assert humanize.intword(10**100) == "1.0 googol"