std :: string pour flotter ou doubler

98

Je suis en train de convertir std::stringà float/double. J'ai essayé:

std::string num = "0.6";
double temp = (double)atof(num.c_str());

Mais il renvoie toujours zéro. D'autres moyens?

Max Frai
la source
3
Résistez à l'envie de suringénierie quelque chose qui a déjà été inventé il y a dix ans.
haavee le
1
êtes-vous sûr de le produire correctement? Il ne devrait pas céder zéro
Johannes Schaub - litb
1
de plus, vous n'avez pas besoin de lancer atof, il renvoie déjà un double.
AlbertoPL
Je suis sûr. Le débogueur me montre 0. Et le résultat est 0. Plate-forme: Linux.
Max Frai
13
Êtes-vous sûr d'avoir installé la bonne langue? essayez "0,6" ou setlocale (LC_NUMERIC, "C");
Johannes Schaub - litb

Réponses:

125
std::string num = "0.6";
double temp = ::atof(num.c_str());

Le fait-il pour moi, c'est une syntaxe C ++ valide pour convertir une chaîne en double.

Vous pouvez le faire avec le stringstream ou boost :: lexical_cast mais ceux-ci viennent avec une pénalité de performance.


Ahaha vous avez un projet Qt ...

QString winOpacity("0.6");
double temp = winOpacity.toDouble();

Remarque supplémentaire:
si les données d'entrée sont a const char*, ce QByteArray::toDoublesera plus rapide.

TimW
la source
7
boost :: lexical_cast est en streaming.
TimW
1
Vous ne pouvez généralement pas dire qu'ils viennent avec une pénalité de performance, je pense. Pensez à ce qui se passe quand juste avant vous avez un cin >> num ;. L'utilisateur devrait taper très rapidement (rly jon skeet like) pour ne jamais remarquer que lexical_cast en millisecondes est plus lent :) Cela dit, je crois qu'il y a des tâches où lexical_cast est juste trop performant :)
Johannes Schaub - litb
3
Pour cette solution, que fait le :: devant atof ()? De quoi a-t-il besoin pour être là?
sivabudh
4
@ShaChris Parce que je veux m'assurer que j'utilise la fonction atof de l'espace de noms global.
TimW
1
dépend de la locale actuelle
nmr
104

La bibliothèque standard (C ++ 11) offre la fonctionnalité souhaitée avec std::stod:

std::string  s  = "0.6"
std::wstring ws = "0.7"
double d  = std::stod(s);
double dw = std::stod(ws);

Généralement pour la plupart des autres types de base, voir <string> . Il existe également de nouvelles fonctionnalités pour les chaînes C. Voir<stdlib.h>

ManuelSchneid3r
la source
4
J'aime cette solution, mais il semble que ce soit uniquement à partir de C ++ 11. Donc pas disponible sur mon SDK.
pamplemousse_mk2
C'est formidable de savoir que le comité des normes C ++ a ajouté cela. ostringstreamen soi était tout simplement trop long à taper, et encore moins à utiliser ..
bobobobo
4
Pour les floats (comme demandé dans la question que j'ai trouvée avec google en tapant "c ++ string to float"), il faut utiliser std :: stof.
Étienne
1
Juste une note que cela peut lever des exceptions: std :: invalid_argument (si la conversion a échoué) std :: out_of_range (si hors de la plage)
Jason Doucette
3
Attention l'acheteur, cela dépend de la localisation actuelle.
nmr
29

Le casting lexical est très agréable.

#include <boost/lexical_cast.hpp>
#include <iostream>
#include <string>

using std::endl;
using std::cout;
using std::string;
using boost::lexical_cast;

int main() {
    string str = "0.6";
    double dub = lexical_cast<double>(str);
    cout << dub << endl;
}
Bill Lynch
la source
Merci, ça marche ... Mais c'est une question pour moi: pourquoi mon code ne fonctionne pas.
Max Frai
2
@Johannes Schaub: Basé sur ADL, il pourrait tout aussi bien avoir, les définitions d'utilisation plus ce qu'il utilise réellement apporteront probablement un grand nombre d'éléments std. De plus, lexical_cast est incroyablement lent, donc pas de +1 de ma part.
Une fonctionnalité intéressante de boost :: lexical_cast est la gestion des erreurs. Si une conversion échoue, une exception est levée:try { ... boost::lexical_cast ... } catch (std::exception const& err) { //handle excpetion }
Semjon Mössinger
Pour être plus précis, utilisez catch ( boost::bad_lexical_cast const& err )pour attraper l'exception.
Semjon Mössinger
14

Vous pouvez utiliser std :: stringstream:

   #include <sstream>
   #include <string>
   template<typename T>
   T StringToNumber(const std::string& numberAsString)
   {
      T valor;

      std::stringstream stream(numberAsString);
      stream >> valor;
      if (stream.fail()) {
         std::runtime_error e(numberAsString);
         throw e;
      }
      return valor;
   }

Usage:

double number= StringToNumber<double>("0.6");
Edison Gustavo Muenz
la source
Euh, alors vous pensez que boost :: lexical_cast a une interface terrible, n'est-ce pas? Regardez la réponse de stefanB! Boost fait de même.
kirsche40
@ kirsche40 Cela semble être une bonne alternative pour les personnes qui n'ont pas déjà de dépendances avec Boost (créer une liaison avec Boost uniquement pour convertir une chaîne std :: string en nombres est un peu exagéré!)
Jean-Philippe Jodoin
@ JEan-Phillippe Jodiun J'ai répondu à un commentaire maintenant supprimé, où quelqu'un a recommandé Boost. Je suis conscient que Boost est la plupart du temps exagéré. D'ailleurs, depuis un certain temps maintenant, l'utilisation de Boost est limitée aux compilateurs "plus récents". Les projets plus anciens ne peuvent pas utiliser Boost. Par exemple, ASIO dépend fortement des fonctionnalités C ++ 11 comme std :: addressof, ce qui le rend totalement inutile pour les compilateurs C ++ 98 / C ++ 03. À mon humble avis, lorsque le projet a commencé, il était l'intention de Boost de fournir de nouvelles fonctionnalités «standardisées» pour les anciennes versions de compilateur ... :-(
kirsche40
10

Oui, avec une distribution lexicale. Utilisez un stringstream et l'opérateur <<, ou utilisez Boost, ils l'ont déjà implémenté.

Votre propre version pourrait ressembler à:

template<typename to, typename from>to lexical_cast(from const &x) {
  std::stringstream os;
  to ret;

  os << x;
  os >> ret;

  return ret;  
}
DaClown
la source
7

Vous pouvez utiliser boost lexical cast:

#include <boost/lexical_cast.hpp>

string v("0.6");
double dd = boost::lexical_cast<double>(v);
cout << dd << endl;

Remarque: boost :: lexical_cast lève une exception, vous devez donc être prêt à y faire face lorsque vous passez une valeur non valide, essayez de passer une chaîne ("xxx")

stefanB
la source
5

Si vous ne voulez pas faire glisser tout le boost, utilisez strtod(3)from <cstdlib>- il renvoie déjà un double.

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>

using namespace std;

int main()  {
    std::string  num = "0.6";
    double temp = ::strtod(num.c_str(), 0);

    cout << num << " " << temp << endl;
    return 0;
}

Les sorties:

$ g++ -o s s.cc
$ ./s
0.6 0.6
$

Pourquoi atof () ne fonctionne pas ... sur quelle plate-forme / compilateur êtes-vous?

haavee
la source
L'utilisation d'un stringstream ne nécessiterait pas de boost
jalf
Votre méthode renvoie également zéro. Linux.
Max Frai
3

J'ai eu le même problème sous Linux

double s2f(string str)
{
 istringstream buffer(str);
 double temp;
 buffer >> temp;
 return temp;
}

Ça marche.

Kenn
la source
2
   double myAtof ( string &num){
      double tmp;
      sscanf ( num.c_str(), "%lf" , &tmp);
      return tmp;
   }
dpetek
la source
1
Réponse non valide, comment savoir que la valeur stockée dans num est en fait un nombre à virgule flottante valide? vous ne vérifiez pas le type de retour de sscanf, cela ressemble à un style de codage MS.
1

Cette réponse se confirme dans vos commentaires. Je soupçonne profondément que vous n’affichez pas correctement le résultat.

J'ai eu exactement la même chose qui m'est arrivée une fois. J'ai passé une journée entière à essayer de comprendre pourquoi j'obtenais une mauvaise valeur dans un int 64 bits, pour découvrir que printf ignorait le deuxième octet. Vous ne pouvez pas simplement passer une valeur 64 bits dans printf comme un int.

TED
la source
Je n'utilise pas printf pour voir les résultats ... Et j'utilise cette valeur pour définir l'opacité de la fenêtre, et ma fenêtre est totalement transparente, donc la valeur est 0.
Max Frai
1

La méthode C ++ 11 consiste à utiliser std :: stod et std :: to_string. Les deux fonctionnent dans Visual Studio 11.

BSalita
la source
1

Quant à savoir pourquoi atof()ne fonctionne pas dans la question initiale: le fait qu'elle soit doublée me rend suspect. Le code ne doit pas être compilé sans #include <stdlib.h>, mais si le cast a été ajouté pour résoudre un avertissement de compilation, il atof()n'est pas correctement déclaré. Si le compilateur suppose qu'il atof()renvoie un int, le cast résoudra l'avertissement de conversion, mais cela ne fera pas reconnaître la valeur de retour comme un double.

#include <stdlib.h>
#include <string>

... 
  std::string num = "0.6";
  double temp = atof(num.c_str());

devrait fonctionner sans avertissement.

Iain
la source
0

Plutôt que de faire glisser Boost dans l'équation, vous pouvez conserver votre chaîne (temporairement) en tant que char[]et l'utiliser sprintf().

Mais bien sûr, si vous utilisez de toute façon Boost, ce n'est vraiment pas trop un problème.

Chris Tonkinson
la source
0

Vous ne voulez pas Boost lexical_cast pour la chaîne <-> virgule flottante de toute façon. Ce sous-ensemble de cas d'utilisation est le seul ensemble pour lequel le boost est systématiquement pire que les anciennes fonctions - et ils ont essentiellement concentré tous leurs échecs là-bas, car leurs propres résultats de performances montrent des performances 20-25X PLUS LENT que l'utilisation de sscanf et printf pour de telles conversions.

Google vous-même. boost :: lexical_cast peut gérer quelque chose comme 50 conversions et si vous excluez celles impliquant des # virgules flottantes, c'est aussi bon ou meilleur que les alternatives évidentes (avec l'avantage supplémentaire d'avoir une seule API pour toutes ces opérations). Mais apportez des flotteurs et c'est comme le Titanic frappant un iceberg en termes de performances.

Les anciennes fonctions str-> double dédiées peuvent toutes faire 10000 analyses en quelque chose comme 30 ms (ou mieux). lexical_cast prend environ 650 ms pour faire le même travail.

Zack Yezek
la source
Aucune source? Je l'ai googlé
Blake
0

Mon problème:

  1. Chaîne indépendante des paramètres régionaux à doubler (séparateur décimal toujours '.')
  2. Détection d'erreur si la conversion de chaîne échoue

Ma solution (utilise la fonction Windows _wcstod_l):

// string to convert. Note: decimal seperator is ',' here
std::wstring str = L"1,101";

// Use this for error detection
wchar_t* stopString;

// Create a locale for "C". Thus a '.' is expected as decimal separator
double dbl = _wcstod_l(str.c_str(), &stopString, _create_locale(LC_ALL, "C")); 

if (wcslen(stopString) != 0)
{
    // ... error handling ... we'll run into this because of the separator
}

HTH ... m'a pris assez de temps pour arriver à cette solution. Et j'ai toujours le sentiment de ne pas en savoir assez sur la localisation des cordes et tout ça ...

anhoppe
la source