在libc++和glibc中,解析doubles时字符串流行为不一致

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

本文关键字:字符串 流行 不一致 doubles 解析 libc++ glibc      更新时间:2023-10-16

使用gcc和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,流的故障位ss.fail()未设置,而
  2. 对于clang来说,它已经设置好了

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

此外,在本地,我会使用clang和gcc获得相同的行为,除非我显式地使用带有clang(-stdlib=libc++)的libc++而不是glibc。

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

输入流提取运算符的行为如下所示:

[isream.formatted.athmethy]与插入器一样,这些提取器依赖于区域设置的num_get<>([locale.num.get])对象以执行对输入流数据的解析。这些提取器表现为格式化的输入函数(在[iistream.formatted.reqmts]中描述)。在构造哨兵对象之后,转换发生,就好像由以下代码片段:

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);

在上面的片段中,loc代表basic_ios类的私有成员。

[facet.num.get.virtuals]有点冗长,但相关部分是:

对于双值,函数strtod。

如果该字段表示的值超出了可表示值的范围,则ios_base::failbit被指定为err。

strtod没有在C++标准中指定,而是在C标准中指定。相关位:

7.20.1.3 strtod、strtof和strtelled函数

§10如果结果下溢(7.12.1),函数返回的值的大小不大于返回类型中最小的归一化正数;errno是否获取值ERANGE是实现定义的。

引用的规则:

7.12.1错误条件的处理

§5如果数学结果的大小太小,以至于在没有异常舍入误差的情况下,无法在指定类型的对象中表示数学结果,则结果下溢204)如果结果下溢,则函数返回一个实现定义的值,该值的大小不大于指定类型中的最小归一化正数;如果整数表达式math_ehlandling&MATH_ERRNO为非零,ERRNO是否获取值ERANGE是实现定义的;如果整数表达式math_ehlandling&MATH_ERREXCEPT为非零,是否引发"underflow"浮点异常是实现定义的

204)此处的"底流"一词既包括IEC 60559中的"渐变底流",也包括"冲洗至零"底流。


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

IEEE-754将下溢指定为:

7.5.0(简化)

当检测到微小的非零结果时,应发出下溢异常信号。当指数范围和精度都是无界的情况下计算出的非零结果将严格位于±bemin之间时,就会出现这种情况。

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

实施者应选择如何检测的微小性


因此,回答您的陈述:

我觉得它应该是一致的。

这很好,但下溢的许多行为都是由实现定义的。

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