From 02dc4d2322572cef787c938c1cb3208f7f9c8ae9 Mon Sep 17 00:00:00 2001 From: gaoflow Date: Tue, 16 Jun 2026 16:04:02 +0200 Subject: [PATCH] Carry metric() to the next SI prefix when rounding reaches 1000 metric() chose the SI prefix from floor(log10(value)) on the raw value and then rounded the mantissa to the requested precision. For a value just below a power-of-1000 boundary the rounded mantissa became 1000 while the prefix stayed put, so metric(999.9, "V") returned "1000 V" and metric(999999, "V") returned "1000 kV". That is exactly the "1230K"-style output the function's docstring promises to avoid. After rounding, if the mantissa reaches 1000 and a higher prefix is available (exponent < 30), advance to the next prefix bucket and re-divide. The top of the SI range (quetta) has no higher prefix, so it is left unchanged. Output for values not on a rounding boundary is unaffected. --- src/humanize/number.py | 11 +++++++++-- tests/test_number.py | 4 ++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/humanize/number.py b/src/humanize/number.py index f4dcb05d..0f907cea 100644 --- a/src/humanize/number.py +++ b/src/humanize/number.py @@ -538,14 +538,21 @@ def metric(value: float, unit: str = "", precision: int = 3) -> str: if exponent >= 33 or exponent < -30: return scientific(value, precision - 1) + unit - value /= 10 ** (exponent // 3 * 3) + old_bucket = exponent // 3 * 3 + value /= 10**old_bucket + digits = int(max(0, precision - exponent % 3 - 1)) + if exponent < 30 and round(abs(value), digits) >= 1000: + exponent += 3 - exponent % 3 + new_bucket = exponent // 3 * 3 + value /= 10 ** (new_bucket - old_bucket) + digits = int(max(0, precision - exponent % 3 - 1)) if exponent >= 3: ordinal_ = "kMGTPEZYRQ"[exponent // 3 - 1] elif exponent < 0: ordinal_ = "mμnpfazyrq"[(-exponent - 1) // 3] else: ordinal_ = "" - value_ = format(value, f".{int(max(0, precision - exponent % 3 - 1))}f") + value_ = format(value, f".{digits}f") if not (unit or ordinal_) or unit in ("°", "′", "″"): space = "" else: diff --git a/tests/test_number.py b/tests/test_number.py index cb74fcf0..cd7c73c1 100644 --- a/tests/test_number.py +++ b/tests/test_number.py @@ -254,6 +254,10 @@ def test_clamp(test_args: list[typing.Any], expected: str) -> None: ([1234.56], "1.23 k"), ([12345, "", 6], "12.3450 k"), ([200_000], "200 k"), + ([999.9, "V"], "1.00 kV"), + ([999.99, "V"], "1.00 kV"), + ([999_999, "V"], "1.00 MV"), + ([0.0009999, "V"], "1.00 mV"), ([1e25, "m"], "10.0 Ym"), ([1e26, "m"], "100 Ym"), ([1e27, "A"], "1.00 RA"),