Skip to content

fix: promote abbreviation unit for negative numbers that round up - #799

Open
spokodev wants to merge 1 commit into
adamwdraper:masterfrom
spokodev:fix/negative-abbreviation-promotion
Open

spokodev wants to merge 1 commit into
adamwdraper:masterfrom
spokodev:fix/negative-abbreviation-promotion

Conversation

@spokodev

Copy link
Copy Markdown

Problem

When a negative value rounds up to the next abbreviation boundary, the formatter promotes the unit for positive numbers but not for negative ones, producing malformed output:

numeral(-999999).format('0a')      // actual "-1000k"   expected "-1m"
numeral(-999999999).format('0a')   // actual "-1000m"   expected "-1b"
numeral(-999999).format('0.0a')    // actual "-1000.0k" expected "-1.0m"

The positive mirrors already format correctly, and the existing test data asserts that promotion:

[999999999,'0a','1b'],
[999950,'0.0a','1.0m'],

Only the negative side was never covered.

Root cause

The post-rounding promotion guard in numeral._.numberToFormat compares the signed value:

if (abbr && !abbrForce && Number(int) >= 1000 && abbr !== locale.abbreviations.trillion) {

For a value like -999999, after rounding int === "-1000", so Number(int) >= 1000 is false (signed comparison) and the unit promotion is skipped. The number is still divided down to -1 elsewhere, leaving the now-incorrect k suffix.

Fix

Compare the magnitude instead, restoring symmetry with the positive path:

if (abbr && !abbrForce && Math.abs(Number(int)) >= 1000 && abbr !== locale.abbreviations.trillion) {

Verification

  • Added negative cases to the abbreviation test data (tests/numeral.js). They fail before the change (expected '-1000k' to equal '-1m') and pass after.
  • Applied the change to src/numeral.js; the built numeral.js was regenerated via grunt build.
  • Full Node suite green (grunt test:npm), including the existing positive abbreviation, spaced (0 a), and forced-abbreviation (0,0 ak/am/ab/at) cases.

When a negative value rounds up to the next abbreviation boundary, the
unit was not promoted (k -> m, m -> b), so the formatter emitted
malformed output such as "-1000k" instead of "-1m".

  numeral(-999999).format('0a')      // was "-1000k",   now "-1m"
  numeral(-999999999).format('0a')   // was "-1000m",   now "-1b"
  numeral(-999999).format('0.0a')    // was "-1000.0k", now "-1.0m"

The positive mirrors already format correctly and are asserted by the
existing test data ([999999999,'0a','1b'] and [999950,'0.0a','1.0m']);
only the negative side was uncovered.

Root cause: the post-rounding promotion guard compared the signed value
(Number(int) >= 1000), so for a rounded negative like int === "-1000"
the check is false and the promotion is skipped. Comparing magnitude
(Math.abs(Number(int)) >= 1000) restores symmetry with positives.

Adds negative cases to the abbreviation test data. Full suite passes.
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.

1 participant