Title
stof() should call strtof() and wcstof()
Status
c++17
Section
[string.conversions]
Submitter
Stephan T. Lavavej

Created on 2014-06-14.00:00:00 last changed 89 months ago

Messages

Date: 2014-11-08.16:43:57

Proposed resolution:

This wording is relative to N3936.

  1. Change [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 [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.

Date: 2014-11-08.16:43:57

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

Date: 2014-06-29.12:55:08

[ 2014-06 Rapperswil ]

Marshall Clow will look at this.

Date: 2014-06-14.00:00:00

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.

History
Date User Action Args
2017-07-30 20:15:43adminsetstatus: wp -> c++17
2015-05-22 18:31:21adminsetstatus: ready -> wp
2014-11-08 16:43:57adminsetmessages: + msg7174
2014-11-08 16:43:57adminsetstatus: new -> ready
2014-06-29 12:55:08adminsetmessages: + msg7087
2014-06-14 19:06:15adminsetmessages: + msg7037
2014-06-14 00:00:00admincreate