std :: string для float или double


98

Я пытаюсь преобразовать std::stringв float/double. Я попытался:

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

Но всегда возвращает ноль. Любые другие способы?


3
Не поддавайтесь искушению чрезмерно изобретать то, что было выяснено уже десять лет назад.
Haavee

1
вы уверены, что выводите его правильно? Он не должен давать ноль
Йоханнес Шауб - лит,

1
кроме того, вам не нужно приводить atof, он уже возвращает double.
AlbertoPL

Я уверен. Отладчик показывает мне 0. И результат 0. Платформа: Linux.
Max Frai

13
Вы уверены, что у вас установлен правильный языковой стандарт? попробуйте «0,6» или setlocale (LC_NUMERIC, «C»);
Йоханнес Шауб - лит

Ответы:


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

Для меня это правильный синтаксис C ++ для преобразования строки в double.

Вы можете сделать это с помощью stringstream или boost :: lexical_cast, но это снижает производительность.


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

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

Дополнительное примечание:
если входные данные - это const char*, QByteArray::toDoubleбудет быстрее.


7
boost :: lexical_cast транслируется.
TimW

1
Я думаю, вы не можете вообще сказать, что они идут со снижением производительности. Подумайте, что происходит, когда прямо перед этим у вас есть cin >> num ;. Пользователю придется набирать текст очень быстро (например, rly jon skeet), чтобы когда-либо заметить, что в миллисекундах 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 ++ string to float»), следует использовать std :: stof.
Étienne

1
Замечу, что это может вызвать исключения: std :: invalid_argument (если преобразование не удалось) std :: out_of_range (если вне диапазона)
Джейсон Дусетт,

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, он мог бы с таким же успехом иметь, определения using плюс то, что он фактически использует, вероятно, внесет в область применения огромное количество элементов std. Кроме того, lexical_cast безумно медленный, так что +1 от меня нет.

Приятной особенностью boost :: lexical_cast является обработка ошибок. Если преобразование не удается, try { ... boost::lexical_cast ... } catch (std::exception const& err) { //handle excpetion }
выдается

Чтобы быть более точным, используйте 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 сильно зависит от функций C ++ 11, таких как std :: addressof, что делает его совершенно бесполезным для компиляторов C ++ 98 / C ++ 03. ИМХО, когда проект стартовал, намерением 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-25 раз МЕДЛЕННУЮ, чем при использовании sscanf и printf для таких преобразований.

Погуглите сами. boost :: lexical_cast может обрабатывать примерно 50 преобразований, и если вы исключите те, которые включают #s с плавающей запятой, это будет так же хорошо или лучше, как и очевидные альтернативы (с дополнительным преимуществом наличия единого 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.