Skip to content

Format decimal numbers with BigDecimal instead of Float - #386

Open
gaoflow wants to merge 1 commit into
ruby-i18n:mainfrom
gaoflow:fix-decimal-float-formatting
Open

Format decimal numbers with BigDecimal instead of Float#386
gaoflow wants to merge 1 commit into
ruby-i18n:mainfrom
gaoflow:fix-decimal-float-formatting

Conversation

@gaoflow

@gaoflow gaoflow commented Jul 28, 2026

Copy link
Copy Markdown

Decimal#apply coerces every number to Float before formatting, so the shared Number engine (behind Decimal, Currency and Percent) inherits three defects from that one conversion:

  • Half-up rounding. round_to uses Float#round; CLDR / UTS Export exemplar characters #35 rounds half-even, as do ICU and Babel. Decimal.new("0").apply(2.5) returns "3" (should be "2"), and apply(0.125) at "0.00" returns "0.13" (should be "0.12").
  • Scientific notation. Float#to_s renders 10**15 as "1.0e+15" and split(".") shreds it, so Number.new("#,##0").apply(10**15) returns "1.0e+15" -- invalid output for an exact value. The repo's own Integer formatter already returns "1,000,000,000,000,000" here.
  • Precision loss. Float(12345678901234567890) drops digits before formatting.

The fix routes formatting through BigDecimal and rounds half-even, so rounding is exact and large integers never reach scientific notation. The rounding change touches only exact binary halves (n.5, n/8); artifacts such as 2.675 (not exactly 2.675 in binary) are unchanged. Expected values in the tests were cross-checked against ICU and Babel 2.17.

Tested: the decimal, number, currency and percent suites pass (e.g. bundle exec ruby test/format/decimal/number_test.rb). The currency and percent test files were empty and now cover the shared rounding path.

Decimal#apply coerced every value to Float before formatting, so the
shared Number engine (Decimal, Currency and Percent) inherited three
defects: half-up rounding instead of CLDR's half-even, scientific
notation for magnitudes >= 1e15 (10**15 formatted as "1.0e+15"), and
lost digits above 2**53.

Route through BigDecimal so rounding is exact half-even per UTS ruby-i18n#35 and
large integers format as grouped digits.
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