2403. stof() should call strtof() and wcstof()

Section: 27.4.5 [string.conversions] Status: C++17 Submitter: Stephan T. Lavavej Opened: 2014-06-14 Last modified: 2017-07-30

Priority: 2

View all other issues in [string.conversions].

View all issues with C++17 status.

Discussion:

stof() is currently specified to call strtod()/wcstod() (which converts the given string to double) and then it's specified to convert that double to float. This performs rounding twice, which introduces error. Here's an example written up by James McNellis:

Consider the following number X:

1.999999821186065729339276231257827021181583404541015625 (X)

This number is exactly representable in binary as:

1.111111111111111111111101000000000000000000000000000001
* ^1st                  ^23rd                        ^52nd

I've marked the 23rd and 52nd fractional bits. These are the least significant bits for float and double, respectively.

If we convert this number directly to float, we take the 23 most significant bits:

1.11111111111111111111110

The next bit is a one and the tail is nonzero (the 54th fractional bit is a one), so we round up. This gives us the correctly rounded result:

1.11111111111111111111111

So far so good. But... If we convert X to double, we take the 52 most significant bits:

1.1111111111111111111111010000000000000000000000000000 (Y)

The next bit is a zero, so we round down (truncating the value). If we then convert Y to float, we take its 23 most significant bits:

1.11111111111111111111110

The next bit is a one and the tail is zero, so we round to even (leaving the value unchanged). This is off by 1ulp from the correctly rounded result.

[2014-06 Rapperswil]

Marshall Clow will look at this.

[Urbana 2014-11-07: Move to Ready]

Proposed resolution:

This wording is relative to N3936.

  1. Change 27.4.5 [string.conversions] p4+p6 as indicated:

    float stof(const string& str, size_t* idx = 0);
    double stod(const string& str, size_t* idx = 0);
    long double stold(const string& str, size_t* idx = 0);
    

    -4- Effects: the first twoThese functions call strtof(str.c_str(), ptr), strtod(str.c_str(), ptr), and the third function calls strtold(str.c_str(), ptr), respectively. Each function returns the converted result, if any. […]

    […]

    -6- Throws: invalid_argument if strtof, strtod, or strtold reports that no conversion could be performed. Throws out_of_range if strtof, strtod, or strtold sets errno to ERANGE or if the converted value is outside the range of representable values for the return type.

  2. Change 27.4.5 [string.conversions] p11+p13 as indicated:

    float stof(const wstring& str, size_t* idx = 0);
    double stod(const wstring& str, size_t* idx = 0);
    long double stold(const wstring& str, size_t* idx = 0);
    

    -11- Effects: the first twoThese functions call wcstof(str.c_str(), ptr), wcstod(str.c_str(), ptr), and the third function calls wcstold(str.c_str(), ptr), respectively. Each function returns the converted result, if any. […]

    […]

    -13- Throws: invalid_argument if wcstof, wcstod, or wcstold reports that no conversion could be performed. Throws out_of_range if wcstof, wcstod, or wcstold sets errno to ERANGE.