Skip to content

fix(number_format): round decimal display values half away from zero like Excel#345

Merged
MathNya merged 2 commits into
MathNya:masterfrom
developer0hye:fix/number-format-rounding-upstream
Jul 22, 2026
Merged

fix(number_format): round decimal display values half away from zero like Excel#345
MathNya merged 2 commits into
MathNya:masterfrom
developer0hye:fix/number-format-rounding-upstream

Conversation

@developer0hye

Copy link
Copy Markdown
Contributor

Problem

get_formatted_value() disagrees with Excel's rendering for three common cases:

  1. Integer formats truncate instead of rounding. 107310.6 with #,##0 returns 107,310; Excel shows 107,311. format_straight_numeric_value returned the integer block verbatim whenever the format had no decimals.
  2. Short fractions are multiplied instead of zero-padded. 39.1 with 0.00 returns 39.100: the right-side digits were parsed as an integer and multiplied by 10^width (1 × 100 = 100) rather than padded to 10.
  3. Percentage ties round the wrong way. 1.065 with 0% returns 106%; Excel shows 107%. 1.065 × 100 in binary doubles is 106.4999…, so anything that multiplies first loses the tie. Excel rounds the decimal display value.

Fix

Round the decimal string directly instead of routing values back through binary floats:

  • New round_decimal_string rounds a plain decimal string (as produced by f64::to_string, i.e. shortest round-trip) half away from zero at the format's fractional width, zero-padding short fractions, with digit-carry propagation for 999.95-style rollovers. Exponent-notation input falls back to float formatting.
  • New group_thousands inserts comma separators into the integer part by string, so grouping survives rounding carries (999,999.5 with #,##01,000,000) and values beyond integer range keep their digits.
  • format_as_percentage now multiplies by shifting the decimal point in the string (1.065106.5) before rounding, which preserves decimal ties exactly.

Testing

  • New unit tests in number_formater.rs covering half-away rounding (positive/negative), zero-padding, excess-decimal rounding, currency prefixes, and thousands grouping through rounding carries, plus tie-rounding tests in percentage_formater.rs
  • cargo test — full suite green (94 + 112 + 79 + 4 tests)

🤖 Generated with Claude Code

Decimal display values were truncated toward zero (107310.6 with #,##0
printed 107,310) and short fractions were multiplied instead of padded
(39.1 with 0.00 printed 39.100). Percentages routed ties through binary
doubles (1.065 with 0% printed 106% instead of 107%).

Round the decimal string directly: half away from zero at the format's
fractional width, zero-padding short fractions, with string-based
thousands grouping, and shift percentages by moving the decimal point.

Signed-off-by: Yonghye Kwon <developer.0hye@gmail.com>
@c-git

c-git commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

@developer0hye would you be able to address the two failing tests? The fix doesn't look like it should be too hard. One looks like just to run cargo fmt with nightly rust and the other is just to fix a format string. If you're getting trouble let me and I can give it a try, shouldn't be too hard.

…rmat-args

- Inline format args in round_decimal_string (clippy::uninlined-format-args)
- Apply nightly cargo fmt to number_formater.rs and percentage_formater.rs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@developer0hye

Copy link
Copy Markdown
Contributor Author

@c-git Thanks for the pointers — both were exactly as you described. Fixed in 865316a:

  • formatting: ran nightly cargo fmt (reformatted the if int_digits.is_empty() block in number_formater.rs and the use ordering in percentage_formater.rs).
  • build/clippy: the uninlined-format-args lint — changed format!("{:.*}", decimals, parsed) to format!("{parsed:.decimals$}").

All checks are green now (build stable/beta/nightly/1.88.0 + formatting). No changes to the actual rounding logic. Thanks for the review!

@c-git

c-git commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

I didn't get a chance to actually understand the code. I just noticed that those were easy fixes.

@MathNya

MathNya commented Jul 22, 2026

Copy link
Copy Markdown
Owner

@developer0hye
Thank you.
Since there were no issues with this either, I'll merge it.

@MathNya
MathNya merged commit e3a8ed3 into MathNya:master Jul 22, 2026
5 checks passed
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.

3 participants