Можно ли читать значения infinity или NaN с помощью входных потоков?
у меня есть некоторые входные данные для чтения входным потоком файлов (например):
-365.269511 -0.356123 -Inf 0.000000
когда я использую std::ifstream mystream;
читать из файла в какой-нибудь
double d1 = -1, d2 = -1, d3 = -1, d4 = -1;
(предположим mystream
уже открыт и файл действителен),
mystream >> d1 >> d2 >> d3 >> d4;
mystream
находится в состоянии неудачи. Я бы ожидал
std::cout << d1 << " " << d2 << " " << d3 << " " << d4 << std::endl;
вывод
-365.269511 -0.356123 -1 -1
. Я хотел бы, чтобы он выводил -365.269511 -0.356123 -Inf 0
вместо.
этот набор данных выводился с использованием потоков C++. Почему я не могу сделать обратный процесс (читать в моем выходе)? Как я могу получить функциональность, которую ищу?
От MooingDuck:
#include <iostream>
#include <limits>
using namespace std;
int main()
{
double myd = std::numeric_limits<double>::infinity();
cout << myd << 'n';
cin >> myd;
cout << cin.good() << ":" << myd << endl;
return 0;
}
вход: inf
выход:
inf
0:inf
см. также:http://ideone.com/jVvei
также связано с этой проблемой NaN
разбор, хотя я не даю примеров для он.
я добавил к принятому ответу полное решение по ideone. Он также включает обрезку для "Inf" и "nan", некоторые возможные варианты тех ключевых слов, которые могут исходить из других программ, таких как MatLab.
5 ответов
Edit: чтобы избежать использования структуры обертки вокруг двойника, я заключаю istream
вместо этого в классе оболочки.
к сожалению, я не могу понять, как избежать двусмысленности, созданной добавлением другого метода ввода для double
. Для реализации ниже, я создал структуру оболочку вокруг istream
, и класс-оболочка реализует метод ввода. Метод ввода определяет отрицательность, затем пытается извлечь double. Если это не удается, начинается разбор.
Edit: спасибо sehe за то, что заставил меня проверить условия ошибок лучше.
struct double_istream {
std::istream ∈
double_istream (std::istream &i) : in(i) {}
double_istream & parse_on_fail (double &x, bool neg);
double_istream & operator >> (double &x) {
bool neg = false;
char c;
if (!in.good()) return *this;
while (isspace(c = in.peek())) in.get();
if (c == '-') { neg = true; }
in >> x;
if (! in.fail()) return *this;
return parse_on_fail(x, neg);
}
};
процедура синтаксического анализа была немного сложнее реализовать, чем я сначала думал, но я хотел избежать попыток putback
всю строку.
double_istream &
double_istream::parse_on_fail (double &x, bool neg) {
const char *exp[] = { "", "inf", "NaN" };
const char *e = exp[0];
int l = 0;
char inf[4];
char *c = inf;
if (neg) *c++ = '-';
in.clear();
if (!(in >> *c).good()) return *this;
switch (*c) {
case 'i': e = exp[l=1]; break;
case 'N': e = exp[l=2]; break;
}
while (*c == *e) {
if ((e-exp[l]) == 2) break;
++e; if (!(in >> *++c).good()) break;
}
if (in.good() && *c == *e) {
switch (l) {
case 1: x = std::numeric_limits<double>::infinity(); break;
case 2: x = std::numeric_limits<double>::quiet_NaN(); break;
}
if (neg) x = -x;
return *this;
} else if (!in.good()) {
if (!in.fail()) return *this;
in.clear(); --c;
}
do { in.putback(*c); } while (c-- != inf);
in.setstate(std::ios_base::failbit);
return *this;
}
одно отличие в поведении эта процедура будет иметь по умолчанию double
вход является то, что -
символ не используется, если вход был, например "-inp"
. О неудаче,"-inp"
все еще будет в потоке для double_istream
, но для обычного istream
только "inp"
останется в потоке.
std::istringstream iss("1.0 -NaN inf -inf NaN 1.2");
double_istream in(iss);
double u, v, w, x, y, z;
in >> u >> v >> w >> x >> y >> z;
std::cout << u << " " << v << " " << w << " "
<< x << " " << y << " " << z << std::endl;
вывод вышеуказанного фрагмента в моей системе:
1 nan inf -inf nan 1.2
Edit: добавление" iomanip", как вспомогательный класс. А double_imanip
объект будет действовать как переключатель, когда он появляется более одного раза в .
struct double_imanip {
mutable std::istream *in;
const double_imanip & operator >> (double &x) const {
double_istream(*in) >> x;
return *this;
}
std::istream & operator >> (const double_imanip &) const {
return *in;
}
};
const double_imanip &
operator >> (std::istream &in, const double_imanip &dm) {
dm.in = ∈
return dm;
}
а затем следующий код, чтобы попробовать out:
std::istringstream iss("1.0 -NaN inf -inf NaN 1.2 inf");
double u, v, w, x, y, z, fail_double;
std::string fail_string;
iss >> double_imanip()
>> u >> v >> w >> x >> y >> z
>> double_imanip()
>> fail_double;
std::cout << u << " " << v << " " << w << " "
<< x << " " << y << " " << z << std::endl;
if (iss.fail()) {
iss.clear();
iss >> fail_string;
std::cout << fail_string << std::endl;
} else {
std::cout << "TEST FAILED" << std::endl;
}
вывод из вышеперечисленного:
1 nan inf -inf nan 1.2
inf
редактировать из Drise: я сделал несколько изменений, чтобы принять такие варианты, как Inf и nan, которые изначально не были включены. Я также превратил его в скомпилированную демонстрацию, которую можно просмотреть по адресу http://ideone.com/qIFVo.
обновление при условии простой тестовый случай, который показывает, что Boost Spirit способен обрабатывать все разновидности специальных значений в этой области. См. ниже: повысить дух (FTW).
стандартный
единственная нормативная информация в этой области, которую я смог найти, содержится в разделах 7.19.6.1/7.19.6.2 стандарта C99.
к сожалению, соответствующие разделы последнего стандартного документа C++ (n3337.pdf) не появится, чтобы указать поддержку infinity
, inf
или NaN
таким же образом. (Возможно, мне не хватает сноски, которая относится к спецификации C99/C11?)
библиотеки реализаторы
в 2000 году Apache libstdcxx получил сообщение об ошибке о том,
на
num_get<>
"энтек"do_get()
участники не принимают специальные строки[-]inf[inity]
и[-]nan
в расчет. Фасет сообщает об ошибке при обнаружении таких строк. Видеть 7.19.6.1 и 7.19.6.2 C99 для списка разрешенных строк.
однако последующее обсуждение дало это (по крайней мере, с имени locale
- s) на самом деле было бы незаконным для реализации анализировать специальные значения:
символами в таблице поиска являются " 0123456789abcdefABCDEF+ -". Библиотечный выпуск 221 изменит это на " 0123456789abcdefxABCDEFX+ -". "N" отсутствует в таблице поиска, поэтому Этап 2 num_get:: do_get() не разрешается читать последовательность символов "NaN".
другие ресурсы
securecoding.cert.org ясно заявляет, что следующий "совместимый код" является требуются чтобы избежать парсинга бесконечность или Нэн. Это означает, что некоторые реализации фактически поддерживают это - предполагая, что автор когда-либо тестировал опубликованный код.
#include <cmath>
float currentBalance; /* User's cash balance */
void doDeposit() {
float val;
std::cin >> val;
if (std::isinf(val)) {
// handle infinity error
}
if (std::isnan(val)) {
// handle NaN error
}
if (val >= MaxValue - currentBalance) {
// Handle range error
}
currentBalance += val;
}
Импульс Духа (FTW)
следующий тривиальный пример имеет желаемый результат:
#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main()
{
const std::string input = "3.14 -inf +inf NaN -NaN +NaN 42";
std::vector<double> data;
std::string::const_iterator f(input.begin()), l(input.end());
bool ok = qi::parse(f,l,qi::double_ % ' ',data);
for(auto d : data)
std::cout << d << '\n';
}
выход:
3.14
-inf
inf
nan
-nan
nan
42
резюме / TL; DR
Я склонен сказать, что C99 определяет поведение для *printf / * scanf для включения бесконечность и Нэн. C++11, к сожалению, не указывает его (или даже запрещает его, при наличии именованных локалей).
напишите функцию с такой подписью:
std::istream & ReadDouble(std::istream & is, double & d);
внутри:
- чтение строки из потока с помощью
operator>>
- попробуйте преобразовать строку в double, используя один из различных методов.
std::stod
,boost::lexical_cast
, etc... - если преобразование выполнено успешно, установите double и верните поток.
- если преобразование не удается, проверьте строку на равенство с "inf "или" INF " или что-то еще.
- если тест проходит, устанавливает двойник на бесконечность и возвращает поток, иначе:
- если тест терпит неудачу, установите бит fail в потоке и верните его.
просто прочитайте свои переменные в строку и проанализируйте их. Вы не можете поместить строку в двойные переменные и ожидать, что они будут выведены как строка, потому что если бы это сработало, строки не были бы необходимы.
Seomthing как:
string text;
double d;
while(cin >> text)
{
if(text == "Inf") //you could also add it with negative infinity
{
d = std::numeric_limits<double>::infinity();
}
else
{
d = atof(text.c_str());
}
}
вам придется написать пользовательскую функцию извлечения, так как ваша конкретная реализация явно не обрабатывает их правильно.