我正在使用一个应用程序,该应用程序使用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?
iss
用于输出,oss
用于输入,这两个名称与内容无关,选择它们有些奇怪且令人困惑。 - πάντα ῥεῖ1e-320
在范围[-DBL_MAX, DBL_MAX]
内,即使它不能被精确表示。 - cpplearner