Я использую приложение, которое использует 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, а если нет, как следует использовать потоки C++ для записи переносимого ввода-вывода с плавающей запятой?