std :: рядок для плавання або подвоєння


98

Я намагаюся конвертувати std::stringв float/double. Я намагався:

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

Але це завжди повертає нуль. Будь-які інші способи?


3
Встояти перед бажанням зайвий раз переробити щось, що з’ясувалося вже десять років тому.
haavee

1
Ви впевнені, що правильно вивели його? Це не повинно дати нуля
Йоханнес Шауб - літ

1
додатково, вам не потрібно кидати atof, це вже повертає дубль.
AlbertoPL

Я впевнений. Налагоджувач показує мені 0. А результат 0. Платформа: Linux.
Макс Фрай

13
Ви впевнені, що встановили правильну локаль? спробуйте "0,6" або setlocale (LC_NUMERIC, "C");
Йоханнес Шауб - літб

Відповіді:


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

Це для мене, це дійсний синтаксис C ++ для перетворення рядка у подвійний.

Ви можете зробити це за допомогою stringstream або boost :: lexical_cast, але ті мають штраф за продуктивність.


Ага, у вас проект Qt ...

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

Додаткова примітка:
Якщо вхідні дані a const char*, QByteArray::toDoubleце буде швидше.


7
boost :: lexical_cast - це трансляція.
TimW

1
Я взагалі не можу сказати, що вони приходять із покаранням за виконання, я думаю. Подумайте, що відбувається, коли безпосередньо перед нею ви маєте cin >> num ;. Користувачеві доведеться вводити текст дуже швидко (як Rly Jon skeet like), щоб коли-небудь відзначати, що lexical_cast у мілісекундах повільніший :) Тим не менш, я вважаю, що є завдання, де lexical_cast просто відсмоктує занадто багато продуктивності :)
Йоханнес Шауб - litb

3
Що для цього рішення робить :: перед atof ()? Що потрібно там бути?
sivabudh

4
@ShaChris Оскільки я хочу переконатись, що використовую функцію atof із глобального простору імен.
TimW

1
залежить від поточної локалі
nmr

104

Стандартна бібліотека (C ++ 11) пропонує бажану функціональність із std::stod:

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

Як правило, для більшості інших основних типів див <string>. Є також деякі нові функції для рядків C. Побачити<stdlib.h>


4
Мені подобається це рішення, але, схоже, воно лише з C ++ 11. Тому недоступний на моєму SDK.
pamplemousse_mk2

Це здорово знати , що комітет по стандартизації C ++ додав це. ostringstreamсаме по собі було просто занадто довгим, щоб набирати текст, не кажучи вже про використання ...
bobobobo

4
Для поплавців (як задано у запитанні, яке я знайшов із google, набравши "рядок c ++ для плавання"), слід використовувати std :: stof.
Етьєн,

1
Тільки примітка, що це може спричинити винятки: std :: invalid_argument (якщо перетворення не вдалося) std :: out_of_range (якщо поза діапазоном)
Jason Doucette

3
Остерігайтеся покупця, залежить від поточного регіону.
нмр

29

Лексичний склад дуже приємний.

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

Дякую, це працює .. Але це питання для мене: чому мій код не працює.
Макс Фрай

2
@Johannes Schaub: Заснований на ADL, він міг би також мати, використання визначень плюс те, що він насправді використовує, мабуть, приведе до сфери застосування величезну кількість стандартних елементів. До того ж lexical_cast шалено повільний, тому жодного +1 від мене.

Приємною особливістю boost :: lexical_cast є обробка помилок. Якщо перетворення не вдається, виникає виняток:try { ... boost::lexical_cast ... } catch (std::exception const& err) { //handle excpetion }
Semjon Mössinger

Якщо бути точнішим, використовуйте, catch ( boost::bad_lexical_cast const& err )щоб зловити виняток.
Semjon Mössinger

14

Ви можете використовувати 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;
   }

Використання:

double number= StringToNumber<double>("0.6");

Так, ви думаєте, у boost :: lexical_cast є жахливий інтерфейс, правда? Подивіться на відповідь stefanB! Boost робить те саме.
kirsche40

@ kirsche40 Виглядає як гарна альтернатива для людей, які ще не мають залежностей від Boost (зв’язок із Boost лише для перетворення std :: string в числа трохи переборщує!)
Жан-Філіпп Жодоін

@ JEan-Phillippe Jodiun Я відповів на видалений коментар, де хтось рекомендував Boost. Я усвідомлюю, що Boost більшу частину часу переборює. До речі, деякий час використання Boost обмежене "новішими" компіляторами. У старих проектах не можна використовувати Boost. Наприклад, ASIO сильно залежить від С ++ 11-функцій, таких як std :: addressof, що робить його абсолютно нічим не потрібним для компіляторів C ++ 98 / C ++ 03. IMHO, коли проект стартував, Boost мав намір надати нові "стандартизовані" функції для старих версій компілятора ... :-(
kirsche40

10

Так, з лексичним складом. Використовуйте рядок рядків та оператор <<, або використовуйте Boost, вони це вже реалізували.

Ваша власна версія може виглядати так:

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

  os << x;
  os >> ret;

  return ret;  
}

7

Ви можете використовувати посилення лексичного складу:

#include <boost/lexical_cast.hpp>

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

Примітка: boost :: lexical_cast видає виняток, тому ви повинні бути готові мати справу з ним, коли передаєте недійсне значення, спробуйте передати рядок ("xxx")


5

Якщо ви не хочете перетягувати весь підсилення, перейдіть з strtod(3)from <cstdlib>- це вже повертає дубль.

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

Виходи:

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

Чому atof () не працює ... на якій платформі / компіляторі ви знаходитесь?


Використання струнного струму не вимагало б посилення
jalf

Ваш метод теж повертає нуль. Linux.
Макс Фрай

3

У мене була та сама проблема в Linux

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

це працює.


2
   double myAtof ( string &num){
      double tmp;
      sscanf ( num.c_str(), "%lf" , &tmp);
      return tmp;
   }

1
Недійсна відповідь, звідки ви знаєте, що значення, яке зберігається в num, насправді є дійсним числом із плаваючою комою? ви не перевіряєте тип повернення sscanf, здається стилем кодування MS.

1

Ця відповідь є резервним кодом у ваших коментарях. Я глибоко підозрюю, що ви просто не відображаєте результат належним чином.

У мене колись траплялося саме те саме. Я витратив цілий день, намагаючись зрозуміти, чому я отримую погане значення в 64-розрядному int, лише виявивши, що printf ігнорує другий байт. Ви не можете просто передати 64-бітове значення в printf, як його int.


Я не використовую printf, щоб бачити результати ... І я використовую це значення, щоб встановити непрозорість вікна, і моє вікно повністю прозоре, тому значення дорівнює 0.
Макс Фрай

1

Спосіб C ++ 11 полягає у використанні std :: stod та std :: to_string. Обидва працюють у Visual Studio 11.


1

Що стосується того, чому atof()не спрацьовує оригінальне запитання: той факт, що його роблять подвійним, робить мене підозрілим. Код не повинен компілюватися без #include <stdlib.h>, але якщо приклад був доданий для вирішення попередження про компіляцію, тоді atof()він неправильно оголошений. Якщо компілятор припускає, що atof()повертає int, це призводить до вирішення попередження про перетворення, але це не призведе до того, що значення, що повертається, розпізнається як подвійне.

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

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

повинен працювати без попереджень.


0

Замість того, щоб перетягувати Boost у рівняння, ви можете зберегти рядок (тимчасово) як a char[]та використовувати sprintf().

Але, звичайно, якщо ви все одно використовуєте Boost, це насправді не надто велика проблема.


0

Ви все одно не хочете Boost lexical_cast для рядка <-> з плаваючою комою. Ця підмножина випадків використання є єдиним набором, для якого підвищення постійно гірше, ніж у старих функцій, - і вони в основному зосереджували там усі свої помилки, оскільки їх власні результати продуктивності показують 20-25X ПОВІЛЬНІШУ ефективність, ніж використання sscanf та printf для таких перетворень.

Погугліть це самостійно. boost :: lexical_cast може обробляти щось на зразок 50 перетворень, і якщо виключити ті, що включають числа з плаваючою комою, це так само добре чи краще, як очевидні альтернативи (з додатковою перевагою наявності єдиного API для всіх цих операцій). Але принесіть поплавки та подібні Титаніку, що б’є айсберг з точки зору продуктивності.

Старі, виділені функції str-> double можуть виконувати 10000 аналізів приблизно за 30 мс (або краще). lexical_cast займає приблизно 650 мс, щоб виконати ту саму роботу.


Немає джерела? Я сам погуглив
Блейк

0

Моя проблема:

  1. Локальний незалежний рядок подвоїти (десятковий роздільник завжди '.')
  2. Виявлення помилок, якщо перетворення рядків не вдається

Моє рішення (використовує функцію 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 ... мені знадобилося досить багато часу, щоб дійти до цього рішення. І я все ще маю відчуття, що я недостатньо знаю про локалізацію рядків та інше ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.