From 08d9bcf63c29d75890baec16a6cdaf29fc5741a0 Mon Sep 17 00:00:00 2001 From: Kumar Akash Date: Tue, 3 Nov 2026 00:32:18 +0530 Subject: [PATCH 1/4] fix(number): preserve digits in large decimal strings in intcomma --- src/humanize/number.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/humanize/number.py b/src/humanize/number.py index 52a5356a..3a5c1389 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -184,7 +184,27 @@ def intcomma(value: NumberOrString, ndigits: int | None = None) -> str: if not math.isfinite(float(value)): return _format_not_finite(float(value)) if "." in value: - value = float(value) + # Without an explicit rounding request, grouping a decimal + # string must not lose digits to binary float rounding + # (issue #496: '1234567890123456.78' became '...456.8'). + # Parse plain decimal strings exactly, as the Decimal input + # path already does; anything else (exponents, trailing + # dots) keeps the previous float behaviour. + parts = value.lstrip("+-").split(".") + digits = "0123456789" + if ( + ndigits is None + and len(parts) == 2 + and all(c in digits for c in parts[0]) + and parts[0] + and all(c in digits for c in parts[1]) + and parts[1] + ): + from decimal import Decimal + + value = Decimal(value) + else: + value = float(value) else: value = int(value) elif not isinstance(value, int): From 6b86b677eb133242457a4bb907fade1e965e293c Mon Sep 17 00:00:00 2001 From: Kumar Akash Date: Tue, 3 Nov 2026 00:35:16 +0530 Subject: [PATCH 2/4] test(number): add intcomma regression tests for issue 496 --- tests/test_number.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test_number.py b/tests/test_number.py index 5fb12fa6..1004e106 100644 --- a/tests/test_number.py +++ b/tests/test_number.py @@ -53,6 +53,11 @@ def test_ordinal(test_input: str, expected: str) -> None: (["10311"], "10,311"), (["1000000"], "1,000,000"), (["1234567.1234567"], "1,234,567.1234567"), + # Regression for issue #496: grouping a large decimal string must + # not lose digits to binary float rounding. + (["1234567890123456.78"], "1,234,567,890,123,456.78"), + (["-1234567890123456.78"], "-1,234,567,890,123,456.78"), + (["14308.40"], "14,308.40"), (["1234567.1234567", 0], "1,234,567"), (["1234567.1234567", 1], "1,234,567.1"), (["1234567.1234567", 10], "1,234,567.1234567000"), From a55ec6f90a7e8743b6bf33781d4920925d77b64b Mon Sep 17 00:00:00 2001 From: Kumar Akash Date: Tue, 3 Nov 2026 01:41:01 +0530 Subject: [PATCH 3/4] fix(number): correct indentation mangled by web edit in intcomma --- src/humanize/number.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/humanize/number.py b/src/humanize/number.py index 3a5c1389..2d583dab 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -184,29 +184,29 @@ def intcomma(value: NumberOrString, ndigits: int | None = None) -> str: if not math.isfinite(float(value)): return _format_not_finite(float(value)) if "." in value: - # Without an explicit rounding request, grouping a decimal - # string must not lose digits to binary float rounding - # (issue #496: '1234567890123456.78' became '...456.8'). - # Parse plain decimal strings exactly, as the Decimal input - # path already does; anything else (exponents, trailing - # dots) keeps the previous float behaviour. - parts = value.lstrip("+-").split(".") - digits = "0123456789" - if ( + # Without an explicit rounding request, grouping a decimal + # string must not lose digits to binary float rounding + # (issue #496: '1234567890123456.78' became '...456.8'). + # Parse plain decimal strings exactly, as the Decimal input + # path already does; anything else (exponents, trailing + # dots) keeps the previous float behaviour. + parts = value.lstrip("+-").split(".") + digits = "0123456789" + if ( ndigits is None and len(parts) == 2 and all(c in digits for c in parts[0]) - and parts[0] - and all(c in digits for c in parts[1]) - and parts[1] - ): - from decimal import Decimal + and parts[0] + and all(c in digits for c in parts[1]) + and parts[1] + ): + from decimal import Decimal - value = Decimal(value) + value = Decimal(value) else: - value = float(value) - else: - value = int(value) + value = float(value) + else: + value = int(value) elif not isinstance(value, int): if not math.isfinite(float(value)): return _format_not_finite(float(value)) From 735147d2953fc03469296686f9a4aca99a8f78ec Mon Sep 17 00:00:00 2001 From: Kumar Akash Date: Tue, 3 Nov 2026 01:43:23 +0530 Subject: [PATCH 4/4] fix(number): replace number.py with exact tested file, fixing indentation --- src/humanize/number.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/humanize/number.py b/src/humanize/number.py index 2d583dab..ac16d733 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -193,20 +193,20 @@ def intcomma(value: NumberOrString, ndigits: int | None = None) -> str: parts = value.lstrip("+-").split(".") digits = "0123456789" if ( - ndigits is None + ndigits is None and len(parts) == 2 and all(c in digits for c in parts[0]) - and parts[0] - and all(c in digits for c in parts[1]) - and parts[1] - ): - from decimal import Decimal - - value = Decimal(value) - else: - value = float(value) + and parts[0] + and all(c in digits for c in parts[1]) + and parts[1] + ): + from decimal import Decimal + + value = Decimal(value) else: - value = int(value) + value = float(value) + else: + value = int(value) elif not isinstance(value, int): if not math.isfinite(float(value)): return _format_not_finite(float(value))