gpt4 book ai didi

c++ - 为什么 Clang std::ostream 会写入 std::istream 无法读取的 double 值?

转载 作者:IT老高 更新时间:2023-10-28 23:02:13 27 4
gpt4 key购买 nike

我正在使用一个应用程序,它使用 std::stringstream 从文本文件中读取空格分隔的 double 矩阵。该应用程序使用的代码有点像:

std::ifstream file {"data.dat"};
const auto header = read_header(file);
const auto num_columns = header.size();
std::string line;
while (std::getline(file, line)) {
std::istringstream ss {line};
double val;
std::size_t tokens {0};
while (ss >> val) {
// do stuff
++tokens;
}
if (tokens < num_columns) throw std::runtime_error {"Bad data matrix..."};
}

相当标准的东西。我勤奋地编写了一些代码来制作数据矩阵(data.dat),对每条数据线使用以下方法:

void write_line(const std::vector<double>& data, std::ostream& out)
{
std::copy(std::cbegin(data), std::prev(std::cend(data)),
std::ostream_iterator<T> {out, " "});
out << data.back() << '\n';
}

即使用 std::ostream。但是,我发现应用程序无法使用此方法读取我的数据文件(抛出上述异常),特别是它无法读取 7.0552574226130007e-321

我编写了以下显示行为的最小测试用例:

// iostream_test.cpp

#include <iostream>
#include <string>
#include <sstream>

int main()
{
constexpr double x {1e-320};
std::ostringstream oss {};
oss << x;
const auto str_x = oss.str();
std::istringstream iss {str_x};
double y;
if (iss >> y) {
std::cout << y << std::endl;
} else {
std::cout << "Nope" << std::endl;
}
}

我在 LLVM 10.0.0 (clang-1000.11.45.2) 上测试了这段代码:

$ clang++ --version
Apple LLVM version 10.0.0 (clang-1000.11.45.2)
Target: x86_64-apple-darwin17.7.0
$ clang++ -std=c++14 -o iostream_test iostream_test.cpp
$ ./iostream_test
Nope

我也尝试使用 Clang 6.0.1、6.0.0、5.0.1、5.0.0、4.0.1 和 4.0.0 进行编译,但得到了相同的结果。

使用 GCC 8.2.0 编译,代码可以正常工作:

$ g++-8 -std=c++14 -o iostream_test iostream_test.cpp
$ ./iostream_test.cpp
9.99989e-321

为什么 Clang 和 GCC 之间有区别?这是一个clang bug吗,如果不是,应该如何使用C++流来编写可移植的浮点IO?

最佳答案

如果我们阅读 std::stod throws out_of_range error for a string that should be valid 的答案,我相信 clang 在这里是一致的。它说:

The C++ standard allows conversions of strings to double to report underflow if the result is in the subnormal range even though it is representable.

7.63918•10-313 is within the range of double, but it is in the subnormal range. The C++ standard says stod calls strtod and then defers to the C standard to define strtod. The C standard indicates that strtod may underflow, about which it says “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.” That is awkward phrasing, but it refers to the rounding errors that occur when subnormal values are encountered. (Subnormal values are subject to larger relative errors than normal values, so their rounding errors might be said to be extraordinary.)

Thus, a C++ implementation is allowed by the C++ standard to underflow for subnormal values even though they are representable.

我们可以确认我们依赖 strtod from [facet.num.get.virtuals]p3.3.4 :

  • For a double value, the function strtod.

我们可以用这个小程序进行测试(现场观看):

void check(const char* p) 
{
std::string str{p};

printf( "errno before: %d\n", errno ) ;
double val = std::strtod(str.c_str(), nullptr);
printf( "val: %g\n", val ) ;
printf( "errno after: %d\n", errno ) ;
printf( "ERANGE value: %d\n", ERANGE ) ;

}

int main()
{
check("9.99989e-321") ;
}

结果如下:

errno before: 0
val: 9.99989e-321
errno after: 34
ERANGE value: 34

7.22.1.3p10 中的 C11告诉我们:

The functions return the converted value, if any. If no conversion could be performed, zero is returned. If the correct value overflows and default rounding is in effect (7.12.1), plus or minus HUGE_VAL, HUGE_VALF, or HUGE_VALL is returned (according to the return type and sign of the value), and the value of the macro ERANGE is stored in errno. 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.

POSIX 使用 that convention :

[ERANGE]
The value to be returned would cause overflow or underflow.

我们可以通过 fpclassify 验证它是否正常。 (see it live)。

关于c++ - 为什么 Clang std::ostream 会写入 std::istream 无法读取的 double 值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52410931/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com