Skip to content

Fix ordinal() suffix for negative numbers - #409

Closed
yu2971512385-ui wants to merge 1 commit into
python-humanize:mainfrom
yu2971512385-ui:fix/ordinal-negative
Closed

yu2971512385-ui wants to merge 1 commit into
python-humanize:mainfrom
yu2971512385-ui:fix/ordinal-negative

Conversation

@yu2971512385-ui

Copy link
Copy Markdown

Problem

ordinal() gives every negative number the th suffix:

>>> import humanize
>>> humanize.ordinal(-1)
'-1th'
>>> humanize.ordinal(-3)
'-3th'
>>> humanize.ordinal(-21)
'-21th'

The docstring says the function "works for any integer or anything int() will turn into an integer", and negative integers already reach the suffix table — they just index it with the wrong digit.

Root cause

digit = 0 if value % 100 in (11, 12, 13) else value % 10

Python's % returns a non-negative remainder for a negative left operand, and that remainder belongs to a different digit: -1 % 10 == 9, -3 % 10 == 7, -21 % 10 == 9. Every one of those lands on a th entry, so the bug is invisible except that the answer is always th.

Fix

Index the table with the last digits of the magnitude. The 11/12/13 exception keeps working because it is applied to the same magnitude:

>>> humanize.ordinal(-1), humanize.ordinal(-2), humanize.ordinal(-3)
('-1st', '-2nd', '-3rd')
>>> humanize.ordinal(-11), humanize.ordinal(-12), humanize.ordinal(-13)
('-11th', '-12th', '-13th')
>>> humanize.ordinal(-21), humanize.ordinal(-111)
('-21st', '-111th')

Positive values, non-numeric input and the non-finite handling are untouched.

This matches how the module already treats signs elsewhere — intword(-1500000)'-1.5 million' and fractional(-1.3)'-1 3/10' both keep the sign on the number and format the magnitude.

Tests

Nine negative cases added to the existing test_ordinal parametrisation, covering the plain digits, the 11/12/13 exception and a value past 100. Verified they fail before the change (4 failed, 246 passed) and pass after (250 passed).

Python's % on a negative operand returns the remainder of the wrong
digit (-1 % 10 == 9), so every negative value picked the "th" suffix:
ordinal(-1) returned "-1th" and ordinal(-3) returned "-3th". Take the
last digits of the magnitude instead, which keeps the 11/12/13
exception working for negative values too.
@hugovk

hugovk commented Sep 22, 2026

Copy link
Copy Markdown
Member

Duplicate of #405 and #321 and lots more.

Check for duplicates next time.

@hugovk hugovk closed this Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants