Подтвердить что ты не робот

Возможно ли считывание значений бесконечности или 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.

Этот набор данных был выведен с использованием потоков С++. Почему я не могу сделать обратный процесс (прочитанный в моем выпуске)? Как я могу получить функциональность, которую я ищу?

Из 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 синтаксическим разбором, хотя я не приводил примеры для нее.

Я добавил к принятому ответу полное решение по идеону. Он также включает в себя paring для "Inf" и "nan", некоторые возможные варианты для тех ключевых слов, которые могут быть получены из других программ, таких как MatLab.

4b9b3361

Ответ 1

Изменить: Чтобы избежать использования структуры обертки вокруг двойника, я включаю istream в класс оболочки.

К сожалению, я не могу понять, как избежать двусмысленности, созданной путем добавления другого метода ввода для double. Для реализации ниже я создал структуру оболочки вокруг istream, а класс-оболочка реализует метод ввода. Метод ввода определяет отрицательность, затем пытается извлечь двойной. Если это не удается, он начинает разбор.

Изменить: Спасибо, что заставило меня лучше проверить условия ошибок.

struct double_istream {
    std::istream &in;

    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

Изменить: Добавление "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 = &in;
    return dm;
}

И затем следующий код, чтобы попробовать:

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.

Ответ 2

Обновление. При условии простого тестового примера, который показывает, что Boost Spirit способен обрабатывать все разновидности специальных значений в этой области. См. Ниже: Boost Spirit (FTW).

Стандартная

Единственная нормативная информация в этой области, которую я смог найти, приведен в разделах 7.19.6.1/7.19.6.2 стандарта C99.

К сожалению, соответствующие разделы последнего документа С++ (n3337.pdf), как представляется, не указывают поддержку для infinity, inf и /NaN таким же образом. (Возможно, мне не хватает сноски, которая относится к спецификации C99/C11?)

Библиотечные реализации

В 2000 году Apache libstdcxx получил отчет об ошибке, указав

Члены num_get<> facet 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 четко заявляет, что следующий "Соответствующий код" требуется, чтобы избежать разбора бесконечности или NaN. Это подразумевает, что некоторые реализации фактически поддерживают это - если автор когда-либо тестировал опубликованный код.

#include <cmath>

float currentBalance; /* User 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;
}

Boost Spirit (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, чтобы включить бесконечность и NaN. С++ 11, к сожалению, не указывает его (или даже запрещает его при наличии названных локалей).

Ответ 3

Напишите функцию с такой сигнатурой:

std::istream & ReadDouble(std::istream & is, double & d);

Внутри него вы:

  • Прочитайте строку из потока с помощью operator>>
  • Попробуйте преобразовать строку в double, используя один из различных методов. std::stod, boost::lexical_cast и т.д.
  • Если преобразование завершается успешно, установите двойной и верните поток.
  • Если преобразование завершается неудачно, проверьте строку для равенства с "inf" или "INF" или что-то еще.
  • Если тест проходит, установите значение double в бесконечность и верните поток, иначе:
  • Если тест завершился неудачно, установите бит сбоя в потоке и верните его.

Ответ 4

Просто прочитайте переменные в строке и проанализируйте их. Вы не можете поместить строку в двойные переменные и ожидать, что они будут выводиться как строка, потому что если она сработает, строки не нужны.

Seomthing like:

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());
    }
}

Ответ 5

Вам нужно будет написать пользовательскую функцию извлечения, так как ваша конкретная реализация явно не справляется с ними правильно.