在 libc++ 和 glibc 中解析双精度时不一致的字符串流行为

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

使用 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
}

...我有不同的行为:

  1. 对于 gcc,流的 failbit ss.fail() 未设置,而
  2. 为 clang 设置

可能需要注意的是,在这两种情况下,errno 都设置为 ERANGE

此外,我在本地使用 clang 和 gcc 时得到相同的行为,除非我明确使用 libc++ 和 clang (-stdlib=libc++) 而不是 glibc。

我不确定正确的行为是什么,但我觉得应该保持一致。

输入流提取操作符的行为指定如下:

[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] 有点冗长,但相关部分是:

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在C++标准中没有规定,在C标准中规定。相关位:

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.

参考规则:

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.


虽然 C++ 没有指定浮点运算的表示方式,但您的系统可能使用 IEEE-754 (IEC 60559)。

IEEE-754 指定下溢为:

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.

其中 ±bemin 是最接近零的正值或负值 normal。它还说:

The implementer shall choose how tininess is detected


所以,回答你的陈述:

it feels to me like it should be consistent.

那会很好,但是下溢周围的许多行为都指定为实现定义的。

坦率地说,输入流 API 是受限的,因为它不能保证在检测到下溢的情况下访问舍入值,也不能提供区分下溢的方法来自其他失败的失败。