Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions millify/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ def millify(n, precision=0, drop_nulls=True, prefixes=[]):
millidx = max(0, min(len(millnames) - 1,
int(math.floor(0 if n == 0 else math.log10(abs(n)) / 3))))
result = '{:.{precision}f}'.format(n / 10**(3 * millidx), precision=precision)
# Rounding to ``precision`` digits can push the mantissa up to 1000
# (e.g. 999999 -> "1000k"); carry it into the next unit so the result
# stays below 1000 ("1M") while a larger suffix is available.
if abs(float(result)) >= 1000 and millidx < len(millnames) - 1:
millidx += 1
result = '{:.{precision}f}'.format(n / 10**(3 * millidx),
precision=precision)
if drop_nulls:
result = remove_exponent(Decimal(result))
return '{0}{dx}'.format(result, dx=millnames[millidx])
Expand Down
32 changes: 32 additions & 0 deletions test_millify.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Tests for millify()."""
import pytest

from millify import millify


@pytest.mark.parametrize(
"n, kwargs, expected",
[
(0, {}, "0"),
(999, {}, "999"),
(1000, {}, "1k"),
(1000000, {}, "1M"),
(1234567, {"precision": 2}, "1.23M"),
(-1500, {"precision": 1}, "-1.5k"),
# Rounding must carry into the next unit instead of producing
# an out-of-range mantissa like "1000k".
(999999, {}, "1M"),
(999500, {}, "1M"),
(999999, {"precision": 2}, "1M"),
(-999999, {}, "-1M"),
(999999999, {}, "1B"),
],
)
def test_millify(n, kwargs, expected):
assert millify(n, **kwargs) == expected


def test_millify_no_overflow_at_largest_unit():
# No larger suffix than "Y" exists, so the mantissa is left as-is
# rather than carrying past the table.
assert millify(10 ** 27, precision=0).endswith("Y")