Skip to content

Decimal precision check does work proportional to the text, not to what decimal holds #276

Closed

Description

@CaffeinatedCoder

On main (1353260, unreleased), DecimalLiteral.Represents (src/CodoMetis.ValueRanges/Internals/DecimalLiteral.cs, from #166 for #115) sizes its work by the supplied text instead of by what decimal can hold:

  1. The digit buffer is stack-allocated at the text's length (line 59). A long element that decimal.Parse accepts exhausts the stack, which no handler can catch, so it reaches TryParse and JSON too. Same class as A deeply nested value set literal overflows the stack and kills the process #248.
  2. The powers of ten (lines 71 and 80) take their exponent from the text, so a short exponent literal costs work that grows superlinearly with the exponent.
  3. The exponent is read with int.Parse (line 53), which throws OverflowException where Parse documents FormatException.

Every entry point is affected: DecimalRange, RangeSet<DecimalRange>, DecimalSet, DecimalSet<T>, in a literal, JSON, TypeConverter and the EF Core JSON document.

Fix: normalize before any arithmetic (strip leading and trailing zeros, adjust the spelled scale) and refuse when the significant digits or the scale exceed what decimal can hold, so the buffer and the powers are bounded by a constant; read the exponent with TryParse. Review the sibling readers (FloatLiteral, IntegerLiteral, UuidLiteral, IsoTemporal) for the same pattern.

Activity

  1. added this to the 10.0.0 milestone on Oct 19, 2026
  2. added
    bugSomething isn't working
    area/rangesRange unions, variants and the shape engines
    area/value-setsValue set families and wrapper arities
    area/jsonSystem.Text.Json and TypeConverter wire forms
    area/literalsParse, TryParse and formatting
    package/coreAffects CodoMetis.ValueRanges
    on Oct 19, 2026
  3. CaffeinatedCoder commented on Oct 19, 2026

    @CaffeinatedCoder
    OwnerAuthor

    Another symptom (reported by the user): {0e-10000000} ran for more than five seconds before the process was killed. BigInteger.Pow expanded the exponent even though the value is zero. Measured on main 1353260 through DecimalSet.TryParse on a 1 MB thread:

    Element Before After
    0e-10000000 still running after 5 s 9 ms, reads 0
    1e-1000000 154 ms, allocates 15 MB 0 ms, refused
    0e99999999999 OverflowException from Parse reads 0 (9.1.0 read 0 too)
    1e-99999999999 OverflowException from Parse FormatException
    0. + 1M zeros + 1 stack overflow, process killed 12 ms, refused
    1. + 2M zeros stack overflow, process killed 25 ms, reads 1

    Fix on fix/276-decimal-literal-bounds: Represents reduces both sides to a significand with no trailing zero times a power of ten, then compares two integers. It counts significant digits before keeping any and refuses more than 29, the most a decimal mantissa can have. It refuses an exponent past ±28. That leaves one pass over the text plus a constant, with no stack buffer and no BigInteger. The exponent is read with TryParse. A differential run of the old and new check over 2.5 million random texts that decimal.Parse accepts found 0 mismatches.

    Sibling readers: none has the pattern. FloatLiteral uses BigInteger.Pow only in Format, and there the exponent comes from the value's binary exponent (at most about 1,100), not from the text. Its parse side is double.Parse plus linear scans. IntegerLiteral stops at the first overflow, UuidLiteral uses a fixed 16-byte buffer, and IsoTemporal parses fixed two-digit slices.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/jsonSystem.Text.Json and TypeConverter wire formsarea/literalsParse, TryParse and formattingarea/rangesRange unions, variants and the shape enginesarea/value-setsValue set families and wrapper aritiesbugSomething isn't workingpackage/coreAffects CodoMetis.ValueRangesseverity/criticalData loss, corruption, or silently wrong results in common use

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions