Skip to content

Make formatWithOptions decimal-exact (drop the Double round-trip) #14

@Merkost

Description

@Merkost

Background

The options-based formatting engine, CurrencyFormatter.formatWithOptions(amount: String, …), is Double-based end to end:

  • normalizedAmount.toDoubleOrNull()CurrencyFormatter.kt:336
  • absAmount.toDoubleOrNull()CurrencyFormatter.kt:418
  • formatDecimal(value: Double, …) rounds via round(value * factor) / factorCurrencyFormatter.kt:599

So any input is reduced to Double before formatting. This includes the new kurrency-deci formatWithOptions(Deci, …) overloads: Deci.toString() is exact, but the shared engine immediately parses it to Double, so very large / high-precision Deci values are not formatted with exact decimal precision. (Surfaced in review of #13; documented there as a caveat.)

This is inconsistent with the minor-units API (0.3.1), which is deliberately string-based to avoid exactly this Double round-trip.

Impact

Low in practice — Double is exact for integers up to 2^53 (~90 trillion with 2 decimals), so normal currency amounts are unaffected. It matters only at extreme magnitude/precision, and specifically undercuts callers who chose Deci for guaranteed exactness.

Approaches

  1. Full engine (preferred): make formatDecimal and the formatWithOptions path operate on a decimal string (parse → round half-up on the string → group → assemble) instead of Double. Benefits every formatWithOptions caller; this is a careful core refactor with regression risk (rounding, grouping, negatives), so it needs thorough cross-platform tests.
  2. Targeted (partial): route the Deci overloads through the existing exact minor-units path — Deci → round to the currency's fraction scale → Long minor units → formatMinorUnitsWithOptions. Makes just the Deci overloads exact without touching the engine, but caps at Long range (~9.2e18 minor units) and needs a fallback above that.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions