Skip to content

js_atod returns NaN for incomplete exponents instead of backtracking #1259

@davesnx

Description

@davesnx

Description

According to the JavaScript specification, parseFloat("1e"), parseFloat("1e+"), and parseFloat("1e-") should all return 1 by backtracking when no valid digit follows the exponent marker. However, QuickJS returns NaN instead.

Expected Behavior (per ECMA-262 / TC39 Test262)

parseFloat("1e")   // should return 1
parseFloat("1e+")  // should return 1
parseFloat("1e-")  // should return 1
parseFloat("1E")   // should return 1
parseFloat("42e")  // should return 42
parseFloat("3.14e+") // should return 3.14

Current Behavior

All of the above return NaN.

Root Cause

In dtoa.c, the js_atod function has this code (around line 1534):

c = to_digit(*p);
if (c >= 10)
    goto fail; /* XXX: could stop before the exponent part */

The comment even acknowledges the issue. When there's no valid digit after the exponent marker (and optional sign), the parser should backtrack to before the e character and return the parsed number, not fail entirely.

Proposed Fix

Save the position before entering the exponent parsing block and restore it if no valid digit is found:

const char *p_before_exp = p; /* save position before exponent */
is_bin_exp = (*p == 'p' || *p == 'P');
p++;
exp_is_neg = 0;
if (*p == '+') {
    p++;
} else if (*p == '-') {
    exp_is_neg = true;
    p++;
}
c = to_digit(*p);
if (c >= 10) {
    /* No valid digit after exponent marker: backtrack to before 'e'.
       JavaScript spec: parseFloat("1e") === 1, parseFloat("1e+") === 1 */
    p = p_before_exp;
    goto after_exp;  /* skip exponent processing, continue with result */
}

Then add after_exp: label before the if (p == p_start) check.

References

  • ECMA-262 Section 21.1.1.1 (ToNumber Applied to the String Type)
  • TC39 Test262 test/built-ins/parseFloat/
  • Found while running Test262 tests via quickjs.ml

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