When compiling the following sample with gcc and clang...
#include <sstream>
#include <iostream>
int main() {
double val;
std::stringstream ss("6.93758e-310");
ss >> val;
std::cout << "fail: " << ss.fail() << std::endl
}
...I get different behavior:
ss.fail()
is not set, while It might be relevant to note that in both cases, errno
is set to ERANGE
.
Also, locally I get the same behavior with clang and gcc, unless I explicitly use libc++ with clang (-stdlib=libc++
) instead of glibc.
I am not sure what the correct behavior is, but it feels to me like it should be consistent.
Behaviour of input streams extraction operator is specified as follows:
[istream.formatted.arithmetic] As in the case of the inserters, these extractors depend on the locale’s
num_get<>
([locale.num.get]) object to perform parsing the input stream data. These extractors behave as formatted input functions (as described in [istream.formatted.reqmts]). After a sentry object is constructed, the conversion occurs as if performed by the following code fragment:using numget = num_get<charT, istreambuf_iterator<charT, traits>>; iostate err = iostate::goodbit; use_facet<numget>(loc).get(*this, 0, *this, err, val); setstate(err);
In the above fragment, loc stands for the private member of the basic_ios class.
[facet.num.get.virtuals] is a bit verbose, but relevant parts are:
For a double value, the function strtod.
... if the field represents a value outside the range of representable values, ios_base::failbit is assigned to err.
strtod
is not specified in the C++ standard, but in the C standard. The relevant bit:
7.20.1.3 The strtod, strtof, and strtold functions
§10 If the result underflows (7.12.1), the functions return a value whose magnitude is no greater than the smallest normalized positive number in the return type; whether errno acquires the value ERANGE is implementation-defined.
The referred rule:
7.12.1 Treatment of error conditions
§5 The result underflows if the magnitude of the mathematical result is so small that the mathematical result cannot be represented, without extraordinary roundoff error, in an object of the specified type.204) If the result underflows, the function returns an implementation-defined value whose magnitude is no greater than the smallest normalized positive number in the specified type; if the integer expression math_errhandling & MATH_ERRNO is nonzero, whether errno acquires the value ERANGE is implementation-defined; if the integer expression math_errhandling & MATH_ERREXCEPT is nonzero, whether the ‘‘underflow’’ floating-point exception is raised is implementation-defined
204)The term underflow here is intended to encompass both ‘‘gradual underflow’’as in IEC 60559 and also ‘‘flush-to-zero’’underflow.
Although C++ does not specify how floating point operations are represented, your system probably uses IEEE-754 (IEC 60559).
IEEE-754 specifies underflow as:
7.5.0 (simplified)
The underflow exception shall be signaled when a tiny non-zero result is detected. This shall be when a non-zero result computed as though both the exponent range and the precision were unbounded would lie strictly between ±bemin.
Where ±bemin is the positive or negative normal value closest to zero. It also says:
The implementer shall choose how tininess is detected
So, to answer your statement:
it feels to me like it should be consistent.
That would be nice, but much of the behaviour around underflow is specified to be implementation-defined.
Frankly, the input stream API is constrained, since it doesn't provide a guaranteed access to the rounded value in cases where an underflow has been detected, nor does it provide a way to differentiate an underflow failure from other failures.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With