Skip to content

Conversation

@dangillet
Copy link
Contributor

Fixes #175

Changes proposed in this pull request:

  • Fix the plural form produced by intword by correctly taking into account how rounding is done
  • Improved code readability and performance
  • Fix one small error in the French translation

@codecov
Copy link

codecov bot commented Nov 3, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.51%. Comparing base (50a413a) to head (00b0d3a).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #273   +/-   ##
=======================================
  Coverage   99.51%   99.51%           
=======================================
  Files          11       11           
  Lines         818      820    +2     
=======================================
+ Hits          814      816    +2     
  Misses          4        4           
Flag Coverage Δ
macos-latest 97.80% <100.00%> (+<0.01%) ⬆️
ubuntu-latest 97.80% <100.00%> (+<0.01%) ⬆️
windows-latest 95.85% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@hugovk hugovk added the changelog: Fixed For any bug fixes label Nov 7, 2025
Copy link
Member

@hugovk hugovk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

  • Improved code readability and performance

Yes, much nicer using bisect 👍

main:

python -m timeit -n 10000 -s "from humanize import intword" 'for a in (1_000_000, 3_500_000, 1_000_000_000, 1_200_000_000, 1_000_000_000_000, 6_700_000_000_000, "1_000", "12_400", "12_490", "1_000_000", "-1_000_000", "1_200_000", "1_290_000", "999_999_999", "1_000_000_000", "-1_000_000_000", "2_000_000_000", "999_999_999_999", "1_000_000_000_000", "6_000_000_000_000", "-6_000_000_000_000", "999_999_999_999_999", "1_000_000_000_000_000", "1_300_000_000_000_000", "-1_300_000_000_000_000", "3_500_000_000_000_000_000_000", "8_100_000_000_000_000_000_000_000_000_000_000", "-8_100_000_000_000_000_000_000_000_000_000_000", "-8.1 quintilliards", 1_000_000_000_000_000_000_000_000_000_000_000_000, 1_100_000_000_000_000_000_000_000_000_000_000_000, 2_100_000_000_000_000_000_000_000_000_000_000_000): intword(a)'
Found existing alias for "python". You should use: "p"
10000 loops, best of 5: 42 usec per loop

PR@

python -m timeit -n 10000 -s "from humanize import intword" 'for a in (1_000_000, 3_500_000, 1_000_000_000, 1_200_000_000, 1_000_000_000_000, 6_700_000_000_000, "1_000", "12_400", "12_490", "1_000_000", "-1_000_000", "1_200_000", "1_290_000", "999_999_999", "1_000_000_000", "-1_000_000_000", "2_000_000_000", "999_999_999_999", "1_000_000_000_000", "6_000_000_000_000", "-6_000_000_000_000", "999_999_999_999_999", "1_000_000_000_000_000", "1_300_000_000_000_000", "-1_300_000_000_000_000", "3_500_000_000_000_000_000_000", "8_100_000_000_000_000_000_000_000_000_000_000", "-8_100_000_000_000_000_000_000_000_000_000_000", "-8.1 quintilliards", 1_000_000_000_000_000_000_000_000_000_000_000_000, 1_100_000_000_000_000_000_000_000_000_000_000_000, 2_100_000_000_000_000_000_000_000_000_000_000_000): intword(a)'
Found existing alias for "python". You should use: "p"
10000 loops, best of 5: 35.3 usec per loop

---

This also 



if not largest_ordinal and rounded_value * power == powers[ordinal + 1]:
# After rounding, we end up just at the next power
ordinal += 1
power = powers[ordinal]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't used:

Suggested change
power = powers[ordinal]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a lot of refactoring. Thanks for noticing that this was still here. You're 100% right. It's not even needed anymore. :)


singular, plural = human_powers[ordinal]
unit = _ngettext(singular, plural, math.ceil(rounded_value))
return f"{negative_prefix}{rounded_value} {unit}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this introduces a regression?

Before:

>>> intword(1234567, "%.0f")
1 million

PR:

>>> intword(1234567, "%.0f")
1.0 million

Try this:

Suggested change
return f"{negative_prefix}{rounded_value} {unit}"
return f"{negative_prefix}{format % rounded_value} {unit}"

And we could add some test cases like this to the main test_intword():

        (["1234567", "%.0f"], "1 million"),
        (["1234567", "%.1f"], "1.2 million"),
        (["1234567", "%.2f"], "1.23 million"),
        (["1234567", "%.3f"], "1.235 million"),
        (["999500", "%.0f"], "1 million"),
        (["999499", "%.0f"], "999 thousand"),

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good catch! I added what you suggested. Thank you!

Remove unused code.
@hugovk hugovk changed the title Fix intword Fix plural form for intword and improve performance Nov 7, 2025
@hugovk hugovk merged commit 38c9968 into python-humanize:main Nov 7, 2025
37 checks passed
@hugovk
Copy link
Member

hugovk commented Nov 7, 2025

Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog: Fixed For any bug fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

intword() decides plural or no plural after rounding the number

2 participants