Skip to content

Commit

Permalink
Added intsuffix
Browse files Browse the repository at this point in the history
  • Loading branch information
ZuluPro committed Apr 14, 2022
1 parent fb06292 commit 8e3128a
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/humanize/__init__.py
Expand Up @@ -8,6 +8,7 @@
fractional,
intcomma,
intword,
intsuffix,
ordinal,
scientific,
)
Expand Down Expand Up @@ -38,6 +39,7 @@
"fractional",
"intcomma",
"intword",
"intsuffix",
"naturaldate",
"naturalday",
"naturaldelta",
Expand Down
69 changes: 69 additions & 0 deletions src/humanize/number.py
Expand Up @@ -211,6 +211,75 @@ def intword(value, format="%.1f"):
return str(value)


suffix_powers = [10**x for x in (3, 6, 9, 12, 15, 18, 21, 24, 27)]
suffix_human_powers = (
NS_("k", "k"),
NS_("M", "M"),
NS_("G", "G"),
NS_("T", "T"),
NS_("P", "P"),
NS_("E", "E"),
NS_("Z", "Z"),
NS_("Y", "Y"),
)


def intsuffix(value, format="%.1f"):
"""Converts a large integer to a friendly text representation with only suffix.
Works best for numbers over 1 million. For example, 1_000_000 becomes "1.0 M",
1200000 becomes "1.2 M" and "1_200_000_000" becomes "1.2 T". Supports up
to decillion (33 digits) and googol (100 digits).
Examples:
```pycon
>>> intsuffix("100")
'100'
>>> intsuffix("12400")
'12.4 k'
>>> intsuffix("1000000")
'1.0 M'
>>> intsuffix(1_200_000_000)
'1.2 G'
>>> intsuffix(None) is None
True
>>> intsuffix("1234000", "%0.3f")
'1.234 M'
```
Args:
value (int, float, str): Integer to convert.
format (str): To change the number of decimal or general format of the number
portion.
Returns:
str: Friendly text representation as a string, unless the value passed could not
be coaxed into an `int`.
"""
try:
value = int(value)
except (TypeError, ValueError):
return value

if value < suffix_powers[0]:
return str(value)
for ordinal, power in enumerate(suffix_powers[1:], 1):
if value < power:
chopped = value / float(suffix_powers[ordinal - 1])
if float(format % chopped) == float(10**3):
chopped = value / float(suffix_powers[ordinal])
singular, plural = suffix_human_powers[ordinal]
return (
" ".join([format, _ngettext(singular, plural, math.ceil(chopped))])
) % chopped
else:
singular, plural = suffix_human_powers[ordinal - 1]
return (
" ".join([format, _ngettext(singular, plural, math.ceil(chopped))])
) % chopped
return str(value)


def apnumber(value):
"""Converts an integer to Associated Press style.
Expand Down
36 changes: 36 additions & 0 deletions tests/test_number.py
Expand Up @@ -96,6 +96,42 @@ def test_intword(test_args, expected):
assert humanize.intword(*test_args) == expected


def test_intsuffix_powers():
# make sure that suffix_powers & suffix_human_powers have the same number of items
assert len(number.powers) == len(number.human_powers)


@pytest.mark.parametrize(
"test_args, expected",
[
(["100"], "100"),
(["1000"], "1.0 k"),
(["12400"], "12.4 k"),
(["12490"], "12.5 k"),
(["1000000"], "1.0 M"),
(["1200000"], "1.2 M"),
(["1290000"], "1.3 M"),
(["999999999"], "1.0 G"),
(["1000000000"], "1.0 G"),
(["2000000000"], "2.0 G"),
(["999999999999"], "1.0 T"),
(["1000000000000"], "1.0 T"),
(["6000000000000"], "6.0 T"),
(["999999999999999"], "1.0 P"),
(["1000000000000000"], "1.0 P"),
(["1300000000000000"], "1.3 P"),
(["1400000000000000000"], "1.4 E"),
(["3500000000000000000000"], "3.5 Z"),
(["3600000000000000000000000"], "3.6 Y"),
([None], None),
(["1230000", "%0.2f"], "1.23 M"),
([10**101], "1" + "0" * 101),
],
)
def test_intsuffix(test_args, expected):
assert humanize.intsuffix(*test_args) == expected


@pytest.mark.parametrize(
"test_input, expected",
[
Expand Down

0 comments on commit 8e3128a

Please sign in to comment.