std :: cadena para flotar o duplicar

98

Estoy tratando de convertirme std::stringa float/double. Lo intenté:

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

Pero siempre devuelve cero. ¿Alguna otra forma?

Max Frai
fuente
3
Resista la tentación de sobreingeniería algo que ya se resolvió hace una década.
haavee
1
¿Está seguro de que lo imprime correctamente? No debería ceder cero
Johannes Schaub - litb
1
además, no necesitas lanzar atof, ya devuelve un doble.
AlbertoPL
Estoy seguro. El depurador me muestra 0. Y el resultado es 0. Plataforma: Linux.
Max Frai
13
¿Está seguro de que tiene instalada la configuración regional correcta? intente "0,6" o establezcalocale (LC_NUMERIC, "C");
Johannes Schaub - litb

Respuestas:

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

Lo hace por mí, es una sintaxis válida de C ++ para convertir una cadena en un doble.

Puede hacerlo con stringstream o boost :: lexical_cast, pero esos vienen con una penalización de rendimiento.


Ahaha tienes un proyecto Qt ...

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

Nota adicional:
si los datos de entrada son a const char*, QByteArray::toDoubleserá más rápido.

TimW
fuente
7
boost :: lexical_cast se está transmitiendo.
TimW
1
En general, no se puede decir que vienen con una penalización de rendimiento, creo. Piense en lo que sucede cuando justo antes tiene un cin >> num ;. El usuario tendría que escribir muy rápido (rly jon skeet like) para notar que los milisegundos lexical_cast son más lentos :) Dicho esto, creo que hay tareas en las que lexical_cast simplemente apesta demasiado rendimiento :)
Johannes Schaub - litb
3
Para esta solución, ¿qué hace el :: delante de atof ()? ¿Qué necesita para estar ahí?
sivabudh
4
@ShaChris Porque quiero asegurarme de usar la función atof del espacio de nombres global.
TimW
1
depende de la ubicación actual
RMN
104

La biblioteca estándar (C ++ 11) ofrece la funcionalidad deseada con std::stod:

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

Generalmente para la mayoría de los otros tipos básicos, consulte <string>. También hay algunas características nuevas para las cuerdas C. Ver<stdlib.h>

ManuelSchneid3r
fuente
4
Me gusta esta solución, pero parece que es solo de C ++ 11. Por lo tanto, no está disponible en mi SDK.
pamplemousse_mk2
Es genial saber que el comité de estándares de C ++ agregó esto. ostringstreamen sí era simplemente demasiado tiempo para escribir a máquina, dejar que su uso solo ..
bobobobo
4
Para los flotadores (como se hace en la pregunta que encontré con google escribiendo "c ++ string to float"), se debe usar std :: stof.
Étienne
1
Solo una nota de que esto puede generar excepciones: std :: invalid_argument (si la conversión falló) std :: out_of_range (si está fuera del rango)
Jason Doucette
3
Atención al comprador, depende de la ubicación actual.
nmr
29

El elenco léxico es muy agradable.

#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
fuente
Gracias, funciona ... Pero es una pregunta para mí: por qué mi código no funciona.
Max Frai
2
@Johannes Schaub: Basado en ADL, bien podría haberlo hecho, las definiciones de uso más lo que realmente está usando probablemente traerán al alcance una gran cantidad de elementos estándar. Además, lexical_cast es increíblemente lento, así que no tengo +1 de mi parte.
Una buena característica de boost :: lexical_cast es el manejo de errores. Si falla una conversión, se lanza una excepción:try { ... boost::lexical_cast ... } catch (std::exception const& err) { //handle excpetion }
Semjon Mössinger
Para ser más precisos, use catch ( boost::bad_lexical_cast const& err )para capturar la excepción.
Semjon Mössinger
14

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

Uso:

double number= StringToNumber<double>("0.6");
Edison Gustavo Muenz
fuente
Uhm, entonces crees que boost :: lexical_cast tiene una interfaz terrible, ¿no? ¡Mira la respuesta de stefanB! Boost hace lo mismo.
kirsche40
@ kirsche40 Parece una buena alternativa para las personas que aún no tienen dependencias con Boost (¡vincular con Boost solo para convertir un std :: string en números es un poco exagerado!)
Jean-Philippe Jodoin
@ JEan-Phillippe Jodiun Respondí a un comentario ahora eliminado, donde alguien recomendó Boost. Soy consciente de que Boost es la mayoría de las veces exagerado. Por cierto, desde hace algún tiempo el uso de Boost está restringido a compiladores "más nuevos". Los proyectos más antiguos no pueden usar Boost. Por ejemplo, ASIO depende en gran medida de características de C ++ 11 como std :: addressof, lo que lo hace completamente inútil para los compiladores de C ++ 98 / C ++ 03. En mi humilde opinión, cuando comenzó el proyecto, la intención de Boost era proporcionar nuevas características "estandarizadas" para versiones de compilador más antiguas ... :-(
kirsche40
10

Sí, con tinte léxico. Use un stringstream y el operador <<, o use Boost, ya lo han implementado.

Su propia versión podría verse así:

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

  os << x;
  os >> ret;

  return ret;  
}
DaClown
fuente
7

Puede utilizar el elenco léxico de impulso:

#include <boost/lexical_cast.hpp>

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

Nota: boost :: lexical_cast lanza una excepción, por lo que debe estar preparado para lidiar con eso cuando pase un valor no válido, intente pasar la cadena ("xxx")

StefanB
fuente
5

Si no desea arrastrar todo el impulso, vaya con strtod(3)desde <cstdlib>: ya devuelve un doble.

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

Salidas:

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

¿Por qué atof () no funciona ... en qué plataforma / compilador estás?

haavee
fuente
Usar un stringstream no requeriría impulso
JALF
Tu método también devuelve cero. Linux.
Max Frai
3

Tuve el mismo problema en Linux

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

funciona.

Kenn
fuente
2
   double myAtof ( string &num){
      double tmp;
      sscanf ( num.c_str(), "%lf" , &tmp);
      return tmp;
   }
dpetek
fuente
1
Respuesta inválida, ¿cómo sabe que el valor almacenado en num es en realidad un número de punto flotante válido? no verifica el tipo de retorno de sscanf, parece un estilo de codificación MS.
1

Esta respuesta está respaldando litb en sus comentarios. Tengo profundas sospechas de que simplemente no está mostrando el resultado correctamente.

Una vez me pasó exactamente lo mismo. Pasé un día entero tratando de averiguar por qué obtenía un valor incorrecto en un int de 64 bits, solo para descubrir que printf ignoraba el segundo byte. No puede simplemente pasar un valor de 64 bits a printf como si fuera un int.

TED
fuente
No estoy usando printf para ver los resultados ... Y uso ese valor para establecer la opacidad de la ventana, y mi ventana es completamente transparente, por lo que el valor es 0.
Max Frai
1

La forma de C ++ 11 es usar std :: stod y std :: to_string. Ambos funcionan en Visual Studio 11.

BSalita
fuente
1

En cuanto a por qué atof()no funciona en la pregunta original: el hecho de que esté emitido al doble me hace sospechar. El código no debería compilarse sin #include <stdlib.h>, pero si la conversión se agregó para resolver una advertencia de compilación, entonces atof()no se declara correctamente. Si el compilador asume atof()devuelve un int, fundición que va a resolver el aviso de conversión, pero será no causa el valor devuelto a ser reconocido como un doble.

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

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

debería funcionar sin advertencias.

Iain
fuente
0

En lugar de arrastrar Boost a la ecuación, puede mantener su cadena (temporalmente) como char[]y usar sprintf().

Pero, por supuesto, si está usando Boost de todos modos, realmente no es un gran problema.

Chris Tonkinson
fuente
0

De todos modos, no desea Boost lexical_cast para string <-> punto flotante. Ese subconjunto de casos de uso es el único conjunto en el que el impulso consistentemente es peor que las funciones anteriores, y básicamente concentraron todas sus fallas allí, porque sus propios resultados de rendimiento muestran un rendimiento 20-25X MÁS LENTO que usar sscanf e printf para tales conversiones.

Búscalo tú mismo. boost :: lexical_cast puede manejar algo así como 50 conversiones y si excluye las que involucran números de punto flotante, es tan buena o mejor como las alternativas obvias (con la ventaja adicional de tener una única API para todas esas operaciones). Pero trae flotadores y es como si el Titanic golpeara un iceberg en términos de rendimiento.

Las antiguas funciones str-> double dedicadas pueden realizar 10000 análisis en algo así como 30 ms (o mejor). lexical_cast tarda unos 650 ms en hacer el mismo trabajo.

Zack Yezek
fuente
¿Sin fuentes? Lo busqué en Google: boost.org/doc/libs/1_55_0/doc/html/boost_lexical_cast/…
Blake
0

Mi problema:

  1. Cadena independiente de configuración regional para duplicar (separador decimal siempre '.')
  2. Detección de errores si falla la conversión de cadenas

Mi solución (usa la función de 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 ... me tomó bastante tiempo llegar a esta solución. Y todavía tengo la sensación de que no sé lo suficiente sobre localización de cadenas y esas cosas ...

anhoppe
fuente