Skip to content

Non-scientific format emits strings parser cannot accumulate (positive-exp round-trip) #184

@thedavidmeister

Description

@thedavidmeister

Context

toDecimalString(float, scientific: false) with positive exponent emits digits(coefficient) + exponent trailing-zero characters — a total output length of digits(coefficient) + exponent characters. The parser (parseDecimalFloat) accumulates each character into an int256 coefficient, which overflows around 77 digits regardless of the actual numeric value. Round-trip fails.

Reproduction

Bounded to within the formatter's cap so format itself succeeds, but parse overflows:

// Coefficient is 56 digits; exponent is within MAX_NON_SCIENTIFIC_EXPONENT (1000).
int256 coef = -36252536662599755883997874465192896811584344295850018217;
int32 exp = 900;  // any value where digits(coef) + exp > ~76
Float f = LibDecimalFloat.packLossless(coef, exp);
string memory s = LibFormatDecimalFloat.toDecimalString(f, false);
// s is a ~956-character decimal string: the 56 digits + 900 zeros.
(bytes4 err, Float parsed) = LibParseDecimalFloat.parseDecimalFloat(s);
// err != 0 — parse rejects the string as the coefficient accumulator overflows.

The failure was found by testFormatParseRoundTripScientificFullDomain/testFormatParseRoundTripNonScientificNegExpFullDomain during #182 fuzz development; the non-scientific fuzz had to bound exponent ≤ 0 to avoid it.

Condition

Triggers whenever digits(coefficient) + exponent > ~76 in non-scientific mode with exponent > 0. With coefficient up to int224 (~68 digits), any exponent > ~8 combined with a non-trivial coefficient can trigger it.

Fix directions

  1. Narrow the formatter: have _toNonScientific revert with UnformatableExponent when the projected output length exceeds what the parser can accept. Symmetric with the current MAX_NON_SCIENTIFIC_EXPONENT cap but tighter in the positive direction.
  2. Widen the parser: parse incrementally and collapse trailing zeros into the exponent rather than building one big int256 coefficient. e.g., once the accumulator has ≥ 68 digits, start counting subsequent zeros as exponent adjustments rather than coefficient digits.

(2) is the more useful long-term fix; (1) is a quick correctness patch.

Related

  • Symmetric issue: scientific format near int32.max exponent produces display exponents above int32.max (separate issue).
  • Fuzz coverage today: testFormatParseRoundTripNonScientificNegExpFullDomain fuzzes exponent ∈ [-1000, 0] comprehensively. Full non-scientific domain coverage is blocked on this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions