std :: string do float lub double

98

Próbuję przekonwertować std::stringna float/double. Próbowałem:

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

Ale zawsze zwraca zero. Jakieś inne sposoby?

Max Frai
źródło
3
Oprzyj się pokusie przepracowania czegoś, co wymyślono już dziesięć lat temu.
haavee
1
czy na pewno wypisujesz go poprawnie? Nie powinno dać zera
Johannes Schaub - litb
1
dodatkowo nie musisz rzucać atof, już zwraca double.
AlbertoPL
Jestem pewien. Debugger pokazuje mi 0. A wynik to 0. Platforma: Linux.
Max Frai
13
Czy na pewno masz zainstalowane właściwe ustawienia regionalne? spróbuj "0,6" lub setlocale (LC_NUMERIC, "C");
Johannes Schaub - litb

Odpowiedzi:

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

Czy to dla mnie, poprawną składnią C ++ jest konwertowanie ciągu znaków na podwójny.

Możesz to zrobić za pomocą stringstream lub boost :: lexical_cast, ale wiąże się to z obniżeniem wydajności.


Ahaha, masz projekt Qt ...

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

Dodatkowa uwaga:
jeśli dane wejściowe to a const char*, QByteArray::toDoublebędzie szybsze.

TimW
źródło
7
boost :: lexical_cast jest przesyłany strumieniowo.
TimW
1
Myślę, że generalnie nie można powiedzieć, że wiążą się one z obniżeniem wydajności. Pomyśl o tym, co się dzieje, gdy tuż przed tym masz cin >> num ;. Użytkownik musiałby pisać bardzo szybko (jak rly jon skeet), aby kiedykolwiek zauważyć, że milisekundy lexical_cast jest wolniejsze :) To powiedziawszy, uważam, że są zadania, w których lexical_cast po prostu wysysa zbyt dużo wydajności :)
Johannes Schaub - litb
3
Co robi dla tego rozwiązania :: przed atof ()? Co to musi tam być?
sivabudh
4
@ShaChris Ponieważ chcę się upewnić, że używam funkcji atof z globalnej przestrzeni nazw.
TimW
1
zależy od lokalizacji
nmr
104

Biblioteka standardowa (C ++ 11) oferuje pożądaną funkcjonalność dzięki std::stod:

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

Ogólnie dla większości innych podstawowych typów, zobacz <string>. Jest też kilka nowych funkcji dla łańcuchów C. Widzieć<stdlib.h>

ManuelSchneid3r
źródło
4
Podoba mi się to rozwiązanie, ale wygląda na to, że pochodzi tylko z C ++ 11. Więc niedostępne w moim SDK.
pamplemousse_mk2
To świetnie wiedzieć, że komisja C ++ standardy dodanych w tym. ostringstreamsam w sobie był po prostu zbyt długi, aby pisać, nie mówiąc już o użyciu ...
bobobobo
4
W przypadku pływaków (jak zadałem w pytaniu, które znalazłem w Google, wpisując „c ++ string to float”), należy użyć std :: stof.
Étienne
1
Tylko uwaga, że ​​może to spowodować wyjątki: std :: invalid_argument (jeśli konwersja nie powiodła się) std :: out_of_range (jeśli poza zakresem)
Jason Doucette
3
Kupujący, strzeż się, zależy od aktualnej lokalizacji.
NMR
29

Obsada leksykalna jest bardzo fajna.

#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
źródło
Dzięki, to działa ... Ale to dla mnie pytanie: dlaczego mój kod nie działa.
Max Frai
2
@Johannes Schaub: Opierając się na ADL, równie dobrze mógłby mieć, użycie definicji plus to, czego faktycznie używa, prawdopodobnie wprowadzi w zakres ogromną liczbę standardowych elementów. Ponadto lexical_cast jest niesamowicie powolny, więc nie otrzymuję ode mnie +1.
Fajną cechą boost :: lexical_cast jest obsługa błędów. Jeśli konwersja się nie powiedzie, zostanie wyrzucony wyjątek:try { ... boost::lexical_cast ... } catch (std::exception const& err) { //handle excpetion }
Semjon Mössinger
Aby być bardziej precyzyjnym, użyj catch ( boost::bad_lexical_cast const& err )do wychwycenia wyjątku.
Semjon Mössinger
14

Możesz użyć 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;
   }

Stosowanie:

double number= StringToNumber<double>("0.6");
Edison Gustavo Muenz
źródło
Uhm, więc myślisz, że boost :: lexical_cast ma okropny interfejs, prawda? Spójrz na odpowiedź StefanaB! Boost robi to samo.
kirsche40
@ kirsche40 Wygląda na dobrą alternatywę dla osób, które nie mają jeszcze zależności z Boostem (łączenie z Boostem tylko w celu konwersji std :: string na liczby to trochę przesada!)
Jean-Philippe Jodoin
@ JEan-Phillippe Jodiun Odpowiedziałem na usunięty komentarz, w którym ktoś polecił Boost. Zdaję sobie sprawę, że Boost to przeważnie przesada. Nawiasem mówiąc, od jakiegoś czasu użycie Boost jest ograniczone do "nowszych" kompilatorów. Starsze projekty nie mogą korzystać z funkcji Boost. Na przykład ASIO zależy w dużej mierze od funkcji C ++ 11, takich jak std :: addressof, co czyni go całkowicie bezwartościowym dla kompilatorów C ++ 98 / C ++ 03. IMHO, kiedy projekt zaczynał się, celem Boost było zapewnienie nowych "standardowych" funkcji dla starszych wersji kompilatorów ... :-(
kirsche40
10

Tak, z obsadą leksykalną. Użyj stringstream i operatora << lub użyj Boost, oni już to zaimplementowali.

Twoja własna wersja mogłaby wyglądać następująco:

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

  os << x;
  os >> ret;

  return ret;  
}
DaClown
źródło
7

Możesz użyć wzmocnienia leksykalnego rzutowania:

#include <boost/lexical_cast.hpp>

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

Uwaga: boost :: lexical_cast zgłasza wyjątek, więc powinieneś być przygotowany na poradzenie sobie z tym, gdy przekazujesz nieprawidłową wartość, spróbuj przekazać string ("xxx")

stefanB
źródło
5

Jeśli nie chcesz, aby przeciągnąć na cały impuls, iść z strtod(3)od <cstdlib>- to już wraca podwójnie.

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

Wyjścia:

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

Dlaczego atof () nie działa ... na jakiej platformie / kompilatorze jesteś?

haavee
źródło
Używanie stringstream nie wymagałoby wzmocnienia
jalf
Twoja metoda również zwraca zero. Linux.
Max Frai
3

Miałem ten sam problem w Linuksie

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

to działa.

Kenn
źródło
2
   double myAtof ( string &num){
      double tmp;
      sscanf ( num.c_str(), "%lf" , &tmp);
      return tmp;
   }
dpetek
źródło
1
Nieprawidłowa odpowiedź, skąd wiesz, że wartość przechowywana w num jest w rzeczywistości poprawną liczbą zmiennoprzecinkową? nie sprawdzasz zwracanego typu sscanf, wygląda na to, że jest to styl kodowania MS.
1

Ta odpowiedź jest kopią zapasową w twoich komentarzach. Mam głębokie podejrzenie, że po prostu nie wyświetlasz poprawnie wyniku.

Kiedyś spotkało mnie dokładnie to samo. Spędziłem cały dzień próbując dowiedzieć się, dlaczego otrzymałem złą wartość w 64-bitowym int, tylko po to, by odkryć, że printf ignoruje drugi bajt. Nie możesz po prostu przekazać 64-bitowej wartości do printf, tak jak jest to int.

PRZETRZĄSAĆ
źródło
Nie używam printf, aby zobaczyć wyniki ... I używam tej wartości do ustawienia krycia okna, a moje okno jest w pełni przezroczyste, więc wartość wynosi 0.
Max Frai
1

Sposób C ++ 11 polega na użyciu std :: stod i std :: to_string. Oba działają w programie Visual Studio 11.

BSalita
źródło
1

Co do tego, dlaczego atof()nie działa w pierwotnym pytaniu: fakt, że jest obsadzony podwójnie, budzi we mnie podejrzenia. Kod nie powinien się kompilować bez #include <stdlib.h>, ale jeśli rzutowanie zostało dodane w celu rozwiązania ostrzeżenia o kompilacji, atof()nie jest poprawnie zadeklarowane. Jeśli kompilator zakłada, że atof()zwraca wartość typu int, rzutowanie go rozwiąże ostrzeżenie o konwersji, ale nie spowoduje, że wartość zwracana zostanie rozpoznana jako podwójna.

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

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

powinien działać bez ostrzeżeń.

Iain
źródło
0

Zamiast przeciągać Boost do równania, możesz zachować strunę (tymczasowo) jako a char[]i użyć sprintf().

Ale oczywiście, jeśli i tak używasz Boost, to naprawdę nie jest to zbyt duży problem.

Chris Tonkinson
źródło
0

I tak nie chcesz Boost lexical_cast dla ciągu <-> zmiennoprzecinkowego. Ten podzbiór przypadków użycia jest jedynym zestawem, dla którego wzmocnienie konsekwentnie jest gorsze niż starsze funkcje - i zasadniczo skoncentrowały tam wszystkie swoje awarie, ponieważ ich własne wyniki wydajności pokazują 20-25X MNIEJSZĄ wydajność niż użycie sscanf i printf do takich konwersji.

Google to sam. boost :: lexical_cast może obsłużyć około 50 konwersji, a jeśli wykluczysz te, które zawierają zmiennoprzecinkowe #, będzie to równie dobre lub lepsze, jak oczywiste alternatywy (z dodatkową zaletą posiadania jednego interfejsu API dla wszystkich tych operacji). Ale przynieś pływaki i pod względem wydajności to jak Titanic uderzający w górę lodową.

Wszystkie stare, dedykowane funkcje str-> double mogą wykonać 10000 parsów w czasie około 30 ms (lub lepszym). lexical_cast wykonuje to samo przez około 650 ms.

Zack Yezek
źródło
Brak źródła? Sam go wygooglowałem
Blake
0

Mój problem:

  1. Niezależny od ustawień regionalnych ciąg znaków do podwójnego (separator dziesiętny zawsze „.”)
  2. Wykrywanie błędu w przypadku niepowodzenia konwersji ciągu

Moje rozwiązanie (używa funkcji 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 ... zajęło mi dość długo dotarcie do tego rozwiązania. I nadal mam wrażenie, że nie wiem wystarczająco dużo o lokalizacji ciągów znaków i tak dalej ...

anhoppe
źródło