49 votes

Est-il possible de lire des valeurs infinies ou NaN en utilisant des flux d'entrée?

J'ai une entrée pour être lu par un filestream d'entrée (par exemple):

-365.269511 -0.356123 -Inf 0.000000

Lorsque j'utilise std::ifstream mystream; de lire à partir du fichier de certaines

double d1 = -1, d2 = -1, d3 = -1, d4 = -1;

(à supposer mystream a déjà été ouvert et que le fichier est valide),

mystream >> d1 >> d2 >> d3 >> d4;

mystream est dans l'échec de l'état. Je m'attends à ce

std::cout << d1 << " " << d2 << " " << d3 << " " << d4 << std::endl;

à la sortie

-365.269511 -0.356123 -1 -1. Je sortie -365.269511 -0.356123 -Inf 0 à la place.

Cet ensemble de données était de sortie à l'aide de C++ ruisseaux. Pourquoi ne puis-je pas faire le processus inverse (lu dans mes sortie)? Comment puis-je obtenir les fonctionnalités que je cherche?

De 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;
}

Entrée: inf

Sortie:

inf
0:inf

Voir aussi: http://ideone.com/jVvei

Également liée à ce problème est - NaN l'analyse, même si je ne donne pas d'exemples pour elle.

J'ai ajouté à la accepté de répondre à une solution complète sur ideone. Il comprend également les éplucher pour "Inf" et "nan", certaines variations possibles à ces mots-clés qui peuvent provenir d'autres programmes, tels que MatLab.

13voto

sehe Points 123151

Mise à jour simple cas de test qui montre que Stimuler l'Esprit est capable de gérer toutes les variétés de valeurs spéciales dans ce domaine. Voir ci-dessous: Stimuler l'Esprit (FTW).

La norme

Les seules informations normatives dans ce domaine que j'ai pu trouver dans les sections 7.19.6.1/7.19.6.2 du standard C99.

Malheureusement, les sections correspondantes du dernier C++ standard de document (n3337.pdf) ne s'affiche pas à spécifier la prise en charge pour infinity, inf et ou NaN de la même manière. (Peut-être que je vais manquer une note de bas de page qui fait référence à la C99/C11 spec?)

La bibliothèque des réalisateurs

En 2000, l'Apache libstdcxx reçu un rapport de bug en indiquant

L' num_get<> facette do_get() membres ne parviennent pas à prendre le spécial cordes [-]inf[inity] et [-]nan compte. La facette signale une erreur lors de la rencontre avec ces chaînes. Voir 7.19.6.1 et 7.19.6.2 de C99 pour une liste de chaînes de caractères.

Cependant la discussion qui a suivi a donné (au moins avec nommée locale-s) il serait illégal pour une mise en œuvre pour analyser les valeurs spéciales:

Les caractères dans la table de recherche sont "0123456789abcdefABCDEF+-". Bibliothèque de numéro 221 modifierait que "0123456789abcdefxABCDEFX+-". "N" n'est pas présent dans la table de recherche, de sorte que la phase 2 de num_get<>::do_get() n'est pas autorisé à lire la séquence de caractères "NaN".

D'autres ressources

securecoding.cert.org indique clairement que le "Code Compatible" est nécessaire pour éviter l'analyse de l'infini ou NaN. Cela signifie que certaines implémentations de réellement soutenir que - en supposant que l'auteur jamais testé le code publié.

#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;
}

Stimuler l'Esprit (FTW)

La suite exemple trivial a la sortie désirée:

#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';
}

Sortie:

3.14
-inf
inf
nan
-nan
nan
42

Résumé/TL;DR

Je suis enclin à dire que C99 spécifie le comportement de *printf/*scanf pour inclure l'infini et NaN. C++11, malheureusement, semble ne pas le spécifier (ou même de l'interdire, en présence de nommé les paramètres régionaux).

9voto

Benjamin Lindley Points 51005

Écrire une fonction avec une signature comme ceci:

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

A l'intérieur, vous:

  1. Lire une chaîne de caractères à partir du flux à l'aide de operator>>
  2. Essayez de convertir la chaîne en double à l'aide de l'une des diverses méthodes. std::stod, boost::lexical_cast, etc...
  3. Si la conversion réussit, définissez le double et le retour du flux.
  4. Si la conversion échoue, test de la chaîne de l'égalité avec les "inf" ou "INF" ou quoi que ce soit.
  5. Si le test réussit, définissez le double à l'infini et le retour du flux, d'autre:
  6. Si le test échoue, définissez la échouer de peu sur le flux et de le retourner.

9voto

jxh Points 32720

Edit: Pour éviter l'utilisation d'un wrapper de la structure autour d'un double, je vous joins un istream dans une classe wrapper à la place.

Malheureusement, je suis incapable de comprendre comment éviter l'ambiguïté créée par l'ajout de la méthode d'entrée pour double. Pour la mise en œuvre ci-dessous, j'ai créé un wrapper de la structure autour d'un istream, et la classe wrapper met en œuvre la méthode d'entrée. La méthode d'entrée détermine la négativité, puis tente d'en extraire un double. Si cela échoue, il commence une analyse.

Edit: Merci à sehe pour me permettre de vérifier les conditions d'erreur de mieux.

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

La routine d'analyse est un peu plus délicat à mettre en œuvre que j'ai d'abord pensé qu'il serait, mais je voulais éviter d'essayer d' putback toute une chaîne de caractères.

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

Une différence dans le comportement de cette routine par rapport à la valeur par défaut double d'entrée, c'est que l' - personnage n'est pas consommé si l'entrée a été, par exemple, "-inp". En cas d'échec, "-inp" sera toujours dans le flux en double_istream, mais régulièrement, istream seulement "inp" sera laissé dans le flux.

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;

La sortie de l'extrait ci-dessus sur mon système:

1 nan inf -inf nan 1.2

Edit: Ajout d'un "iomanip" comme classe d'aide. Un double_imanip objet agit comme une bascule lorsqu'il apparaît plus d'une fois dans l' >> chaîne.

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

Et puis le code suivant pour l'essayer:

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

La sortie de la ci-dessus est:

1 nan inf -inf nan 1.2
inf

Edit de Drise: j'ai fait quelques modifications à accepter des variations telles que Inf et nan ce n'était pas à l'origine inclus. J'ai aussi fait un compilé de démonstration, qui peut être consulté à http://ideone.com/qIFVo.

4voto

Blood Points 2899

Il suffit de lire vos variables dans une chaîne et de les analyser. Vous ne pouvez pas mettre une chaîne dans des variables doubles et vous attendre à ce qu'elles soient sorties comme une chaîne, car si cela fonctionnait, les chaînes ne seraient pas nécessaires.

Seomthing comme:

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

0voto

Puppy Points 90818

Vous devrez écrire une fonction d'extraction personnalisée, car votre implémentation spécifique ne les gère clairement pas correctement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X