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
    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