在libc++和glibc中,解析doubles时字符串流行为不一致
Inconsistent stringstream behavior when parsing doubles in libc++ and glibc
使用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
}
我得到了不同的行为:
- 使用gcc,流的故障位
ss.fail()
未设置,而 - 对于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是受限制的,因为在检测到下溢的情况下,它不能保证对舍入值的访问,也不能提供区分下溢故障和其他故障的方法。