Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inconsistent stringstream behavior when parsing doubles in libc++ and glibc

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:

  1. With gcc, the stream's failbit ss.fail() is not set, while
  2. for clang it is set

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.

like image 634
Horstinator Avatar asked Nov 06 '22 20:11

Horstinator


1 Answers

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.

like image 171
eerorika Avatar answered Nov 14 '22 23:11

eerorika