num_get
not fully compatible with strto*
Section: 28.3.4.3.2.3 [facet.num.get.virtuals] Status: C++17 Submitter: Cosmin Truta Opened: 2009-07-04 Last modified: 2017-07-30
Priority: 3
View other active issues in [facet.num.get.virtuals].
View all other issues in [facet.num.get.virtuals].
View all issues with C++17 status.
Discussion:
As specified in the latest draft,
N2914,
num_get
is still not fully compatible with the following C
functions: strtoul
, strtoull
,
strtof
and
strtod
.
In C, when conversion of a string to an unsigned integer type falls
outside the
representable range, strtoul
and strtoull
return
ULONG_MAX
and ULLONG_MAX
, respectively,
regardless
whether the input field represents a positive or a negative value.
On the other hand, the result of num_get
conversion of
negative
values to unsigned integer types is zero. This raises a compatibility
issue.
Moreover, in C, when conversion of a string to a floating-point type falls
outside the representable range, strtof
, strtod
and
strtold
return ±HUGE_VALF
,
±HUGE_VAL
and ±HUGE_VALL
, respectively.
On the other hand, the result of num_get
conversion of such
out-of-range floating-point values results in the most positive/negative
representable value.
Although many C library implementations do implement HUGE_VAL
(etc.) as the highest representable (which is, usually, the infinity),
this isn't required by the C standard. The C library specification makes no
statement regarding the value of HUGE_VAL
and friends, which
potentially raises the same compatibility issue as in the above case of
unsigned integers.
In addition, neither C nor C++ define symbolic constants for the maximum
representable floating-point values (they only do so only for the maximum
representable finite floating-point values), which raises a
usability
issue (it would be hard for the programmer to check the result of
num_get
against overflow).
As such, we propose to adjust the specification of num_get
to
closely follow the behavior of all of its underlying C functions.
[ 2010 Rapperswil: ]
Some concern that this is changing the specification for an existing C++03 function, but it was pointed out that this was underspecified as resolved by issue 23. This is clean-up for that issue in turn. Some concern that we are trying to solve the same problem in both clause 22 and 27.
Bill: There's a change here as to whether val is stored to in an error case.
Pablo: Don't think this changes whether val is stored to or not, but changes the value that is stored.
Bill: Remembers having skirmishes with customers and testers as to whether val is stored to, and the resolution was not to store in error cases.
Howard: Believes since C++03 we made a change to always store in overflow.
Everyone took some time to review the issue.
Pablo: C++98 definitely did not store any value during an error condition.
Dietmar: Depends on the question of what is considered an error, and whether overflow is an error or not, which was the crux of LWG 23.
Pablo: Yes, but given the "zero, if the conversion function fails to convert the entire field", we are requiring every error condition to store.
Bill: When did this happen?
Alisdair: One of the last two or three meetings.
Dietmar: To store a value in case of failure is a very bad idea.
Move to Open, needs more study.
[2011-03-24 Madrid meeting]
Move to deferred
[ 2011 Bloomington ]
The proposed wording looks good, no-one sure why this was held back before. Move to Review.
[2012,Kona]
Move to Open.
THe issues is what to do with -1
. Should it match 'C' or do the "sane" thing.
A fix here changes behavior, but is probably what we want.
Pablo to provide wording, with help from Howard.
[2015-05-06 Lenexa: Move to Ready]
STL: I like that this uses strtof, which I think is new in C99. that avoids truncation from using atof. I have another issue ...
MC: yes LWG 2403 (stof should call strtof)
PJP: the last line is horrible, you don't assign to err, you call setstate(ios_base::failbit). Ah, no, this is inside num_get so the caller does the setstate.
MC: we need all these words. are they the right words?
JW: I'd like to take a minute to check my impl. Technically this implies a change in behaviour (from always using strtold and checking the extracted floating point value, to using the right function). Oh, we already do exactly this.
MC: Move to Ready
6 in favor, none opposed, 1 abstention
Proposed resolution:
Change 28.3.4.3.2.3 [facet.num.get.virtuals] as follows:
Stage 3: The sequence of
char
s accumulated in stage 2 (the field) is converted to a numeric value by the rules of one of the functions declared in the header<cstdlib>
:
- For a signed integer value, the function
strtoll
.- For an unsigned integer value, the function
strtoull
.- For a
float
value, the functionstrtof
.- For a
double
value, the functionstrtod
.- For a
floating-pointlong double
value, the functionstrtold
.The numeric value to be stored can be one of:
- zero, if the conversion function fails to convert the entire field.
ios_base::failbit
is assigned toerr
.- the most positive (or negative) representable value, if the field to be converted to a signed integer type represents a value too large positive (or negative) to be represented in
val
.ios_base::failbit
is assigned toerr
.the most negative representable value or zero for an unsigned integer type, if the field represents a value too large negative to be represented inval
.ios_base::failbit
is assigned toerr
.- the most positive representable value, if the field to be converted to an unsigned integer type represents a value that cannot be represented in
val
.- the converted value, otherwise.
The resultant numeric value is stored in
val
. If the conversion function fails to convert the entire field, or if the field represents a value outside the range of representable values,ios_base::failbit
is assigned toerr
.