This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

### 3456. Pattern used by `std::from_chars` is underspecified

Section: 20.19.3 [charconv.from.chars] Status: New Submitter: Jonathan Wakely Opened: 2020-06-23 Last modified: 2020-09-06

Priority: 3

View other active issues in [charconv.from.chars].

View all other issues in [charconv.from.chars].

View all issues with New status.

Discussion:

The intention of 20.19.3 [charconv.from.chars] p7 is that the `fmt` argument modifies the expected pattern, so that only a specific subset of valid `strtod` patterns are recognized for each format. This is not clear from the wording.

When `fmt == chars_format::fixed` no exponent is to be used, so any trailing characters that match the form of a `strtod` exponent are ignored. For example, `"1.23e4"` should produce the result `1.23` for the fixed format. The current wording says "the optional exponent part shall not appear" which can be interpreted to mean that `"1.23e4"` violates a precondition and so has undefined behaviour!

When `fmt != chars_format::hex` only decimal numbers should be recognized. This means that for any format except scientific, `"0x123"` produces `0.0` (it's invalid when `fmt == chars_format::scientific` because there's no exponent). The current wording only says that when `hex` is used the string has an assumed `"0x"` prefix, so is interpreted as a hexadecimal float, it doesn't say that when `fmt != hex` that the string is not interpreted as a hexadecimal float.

Two alternative resolutions are provided, one is a minimal fix and the other attempts to make it clearer by not referring to a modified version of the C rules.

[2020-07-14; Jonathan fixes the `strtod` call in Option B]

[2020-07-17; Priority set to 3 in telecon]

Proposed resolution:

This wording is relative to N4861.

Option A:

1. Modify 20.19.3 [charconv.from.chars] as indicated:

   >     from_chars_result from_chars(const char* first, const char* last, float& value,
   >                                  chars_format fmt = chars_format::general);
   >     from_chars_result from_chars(const char* first, const char* last, double& value,
   >                                  chars_format fmt = chars_format::general);
   >     from_chars_result from_chars(const char* first, const char* last, long double& value,
   >                                  chars_format fmt = chars_format::general);

   > > -6- Preconditions: `fmt` has the value of one of the enumerators of `chars_format`.

   > > -7- Effects: The pattern is the expected form of the subject sequence in the "C" locale, as described for `strtod`, except that

   > > 1. (7.1) — the sign `'+'` may only appear in the exponent part;

   > > 2. (7.2) — if `fmt` has `chars_format::scientific` set but not `chars_format::fixed`, the otherwise optional exponent part shall appear exponent part is not optional;

   > > 3. (7.3) — if `fmt` has `chars_format::fixed` set but not `chars_format::scientific`, the optional exponent part shall not appear; and there is no exponent part;

   > > 4. (?.?) — if `fmt` is not `chars_format::hex`, only decimal digits and an optional `'.'` appear before the exponent part (if any); and

   > > 5. (7.4) — if `fmt` is `chars_format::hex`, the prefix `"0x"` or `"0X"` is assumed. [Example: The string `0x123` is parsed to have the value `0` with remaining characters `x123`. — end example]

   > > In any case, the resulting `value` is one of at most two floating-point values closest to the value of the string matching the pattern.

Option B:

1. Modify 20.19.3 [charconv.from.chars] as indicated:

   >     from_chars_result from_chars(const char* first, const char* last, float& value,
   >                                  chars_format fmt = chars_format::general);
   >     from_chars_result from_chars(const char* first, const char* last, double& value,
   >                                  chars_format fmt = chars_format::general);
   >     from_chars_result from_chars(const char* first, const char* last, long double& value,
   >                                  chars_format fmt = chars_format::general);

   > > -6- Preconditions: `fmt` has the value of one of the enumerators of `chars_format`.

   > > -7- Effects: The pattern is the expected form of the subject sequence in the "C" locale, as described for `strtod`, except that The pattern is an optional `'-'` sign followed by one of:

   > > 1. (7.1) — the sign `'+'` may only appear in the exponent part `INF` or `INFINITY`, ignoring case;

   > > 2. (7.2) — if `fmt` has `chars_format::scientific` set but not `chars_format::fixed`, the otherwise optional exponent part shall appear if `numeric_limits<T>::has_quiet_NaN` is `true`, `NAN` or `NAN(`n-char-sequenceopt`)`, ignoring case in the `NAN` part, where:

   > >    >     n-char-sequence:
   > >    >            digit
   > >    >            nondigit
   > >    >            n-char-sequence digit
   > >    >            n-char-sequence nondigit

   > >    ;

   > > 3. (7.3) — if `fmt` has `chars_format::fixed` set but not `chars_format::scientific`, the optional exponent part shall not appear; and if `fmt` is equal to `chars_format::scientific`, a sequence of characters matching chars-format-dec exponent-part, where:

   > >    >     chars-format-dec:
   > >    >              fractional-constant
   > >    >              digit-sequence

   > >    ;

   > > 4. (7.4) — if `fmt` is `chars_format::hex`, the prefix `"0x"` or `"0X"` is assumed. [Example: The string `0x123` is parsed to have the value `0` with remaining characters `x123`. — end example]if `fmt` is equal to `chars_format::fixed`, a sequence of characters matching chars-format-dec;

   > > 5. (?.?) — if `fmt` is equal to `chars_format::general`, a sequence of characters matching chars-format-dec exponent-partopt; or

   > > 6. (?.?) — if `fmt` is equal to `chars_format::hex`, a sequence of characters matching chars-format-hex binary-exponent-partopt, where:

   > >    >     chars-format-hex:
   > >    >              hexadecimal-fractional-constant
   > >    >              hexadecimal-digit-sequence

   > >    [Note: The pattern is derived from the subject sequence in the `"C"` locale for `strtod`, with the value of `fmt` limiting which forms of the subject sequence are recognized, and with no `0x` or `0X` prefix recognized. — end note]

   > > For a character sequence `INF`, `INFINITY`, `NAN`, or `NAN(`n-char-sequenceopt`)` the resulting value is obtained as if by evaluating `strtod(string(first, last).c_str(), nullptr)` in the `"C"` locale. In all other cases In any case, the resulting `value` is one of at most two floating-point values closest to the value of the string matching the pattern.