Skip to content

Commit 56f4f71

Browse files
committed
Fix metric() showing an extra significant digit on rounding carry
metric() derives the number of decimal places from the mantissa's exponent, assuming the mantissa keeps its digit count. When rounding carries it up a power of ten (9.999 -> 10.0, 99.99 -> 100) it gains an integer digit and shows one significant figure too many, e.g. metric(9999) returned '10.00 k' instead of '10.0 k'. The existing guard only handled the mantissa reaching 1000 (a full SI-bucket crossing). Detect the carry against the next power of ten and bump the exponent by one, which recomputes the decimal places and, when the mantissa reaches 1000, still crosses into the next bucket exactly as before.
1 parent c3a124c commit 56f4f71

2 files changed

Lines changed: 16 additions & 2 deletions

File tree

src/humanize/number.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,8 +548,13 @@ def metric(value: float, unit: str = "", precision: int = 3) -> str:
548548
old_bucket = exponent // 3 * 3
549549
value /= 10**old_bucket
550550
digits = int(max(0, precision - exponent % 3 - 1))
551-
if exponent < 30 and round(abs(value), digits) >= 1000:
552-
exponent += 3 - exponent % 3
551+
# Rounding to ``digits`` decimal places can carry the mantissa up to the
552+
# next power of ten (e.g. 9.999 -> 10.0, 99.99 -> 100, 999.9 -> 1000),
553+
# which adds an integer digit and would otherwise show one significant
554+
# figure too many. Bump the exponent to absorb the carry -- crossing into
555+
# the next SI bucket when the mantissa reaches 1000 -- and recompute.
556+
if exponent < 30 and round(abs(value), digits) >= 10 ** (exponent % 3 + 1):
557+
exponent += 1
553558
new_bucket = exponent // 3 * 3
554559
value /= 10 ** (new_bucket - old_bucket)
555560
digits = int(max(0, precision - exponent % 3 - 1))

tests/test_number.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,15 @@ def test_clamp(test_args: list[typing.Any], expected: str) -> None:
258258
([1234.56], "1.23 k"),
259259
([12345, "", 6], "12.3450 k"),
260260
([200_000], "200 k"),
261+
# Rounding that carries the mantissa up a power of ten within a
262+
# bucket must not add an extra significant figure (issue: metric
263+
# showed one digit too many for e.g. 9999 -> "10.00 k").
264+
([9999], "10.0 k"),
265+
([99999], "100 k"),
266+
([9.99999], "10.0"),
267+
([-9999], "-10.0 k"),
268+
([9.999, "", 2], "10"),
269+
([999.4], "999"),
261270
([999.9, "V"], "1.00 kV"),
262271
([999.99, "V"], "1.00 kV"),
263272
([999_999, "V"], "1.00 MV"),

0 commit comments

Comments
 (0)