Converti float in stringa con precisione e numero di cifre decimali specificato?


91

Come si converte un float in una stringa in C ++ specificando la precisione e il numero di cifre decimali?

Per esempio: 3.14159265359 -> "3.14"


perché non creare un float temporaneo con la precisione specificata che desideri e quindi convertirlo in stringa?
Gilad

@Gilad Il 'temp float' non ha una 'precisione specificata' e ci sono casi in cui tale approccio fallirà. Questa domanda è fondamentalmente come chiedere "qual è l'equivalente del formato '% .2f'"?
user2864740

1
Vedi stackoverflow.com/questions/14432043/c-float-formatting se questo è solo necessario per IO.
user2864740

Risposte:


133

Un modo tipico sarebbe usare stringstream:

#include <iomanip>
#include <sstream>

double pi = 3.14159265359;
std::stringstream stream;
stream << std::fixed << std::setprecision(2) << pi;
std::string s = stream.str();

Vedi fisso

Usa la notazione a virgola mobile fissa

Imposta il floatfieldflag di formato per il flusso str su fixed.

Quando floatfieldè impostato su fixed, i valori a virgola mobile vengono scritti utilizzando la notazione a virgola fissa: il valore è rappresentato esattamente con tante cifre nella parte decimale come specificato dal campo di precisione ( precision) e senza parte esponente.

e impostare la precisione .


Per conversioni di scopi tecnici , come la memorizzazione di dati in file XML o JSON, C ++ 17 definisce la famiglia di funzioni to_chars .

Supponendo un compilatore conforme (che ci manca al momento della scrittura), si può considerare qualcosa di simile:

#include <array>
#include <charconv>

double pi = 3.14159265359;
std::array<char, 128> buffer;
auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), pi,
                               std::chars_format::fixed, 2);
if (ec == std::errc{}) {
    std::string s(buffer.data(), ptr);
    // ....
}
else {
    // error handling
}

31

Il metodo consueto per fare questo genere di cose è "stampare su una stringa". In C ++ questo significa usare std::stringstreamqualcosa come:

std::stringstream ss;
ss << std::fixed << std::setprecision(2) << number;
std::string mystring = ss.str();

13

Un'altra opzione è snprintf:

double pi = 3.1415926;

std::string s(16, '\0');
auto written = std::snprintf(&s[0], s.size(), "%.2f", pi);
s.resize(written);

Demo . Dovrebbe essere aggiunta la gestione degli errori, cioè il controllo diwritten < 0.


Perché sarebbe snprintfmeglio in questo caso? Spiega ... Certamente non sarà più veloce, produrrà meno codice [Ho scritto un'implementazione printf, è abbastanza compatto con poco meno di 2000 righe di codice, ma con espansioni macro arriva a circa 2500 righe]
Mats Petersson

1
@MatsPetersson Credo fermamente che sarà più veloce. Questo è il motivo per cui ho dato questa risposta in primo luogo.
Columbo

@MatsPetersson Ho eseguito misurazioni (GCC 4.8.1, -O2) che indicano che snprintf sta effettivamente impiegando meno tempo, per il fattore 17/22. Caricherò il codice a breve.
Columbo

@MatsPetersson Il mio benchmark è probabilmente imperfetto, ma la versione stringstream impiega il doppio della versione snprintf a 1000000 iterazioni (Clang 3.7.0, libstdc ++, -O2).

Ho fatto anche alcune misurazioni - e sembrerebbe (almeno su Linux) che sia stringstream che snprintf usino la stessa __printf_fpfunzionalità sottostante - è piuttosto strano che ci voglia molto più tempo stringstream, come non dovrebbe davvero.
Mats Petersson

10

Puoi usare C ++ 20 std::formato la fmt::formatfunzione dalla libreria {fmt} , std::formatsi basa su:

#include <fmt/core.h>

int main()
  std::string s = fmt::format("{:.2f}", 3.14159265359); // s == "3.14"
}

dov'è 2una precisione.


8

Ecco una soluzione che utilizza solo std. Tuttavia, si noti che questo viene solo arrotondato per difetto.

    float number = 3.14159;
    std::string num_text = std::to_string(number);
    std::string rounded = num_text.substr(0, num_text.find(".")+3);

Perché roundedproduce:

3.14

Il codice converte l'intero float in stringa, ma taglia tutti i caratteri 2 caratteri dopo il "."


L'unica cosa è che in questo modo non stai effettivamente arrotondando il numero.
ChrCury78

1
@ ChrCury78 come indicato nella mia risposta, questo si limita a arrotondare per difetto. Se vuoi un arrotondamento normale, aggiungi semplicemente 5 al numero che segue il tuo valore. Ad esempio number + 0.005nel mio caso.
Sebastian R.

3

Qui sto fornendo un esempio negativo in cui vuoi evitare quando converti il ​​numero mobile in stringhe.

float num=99.463;
float tmp1=round(num*1000);
float tmp2=tmp1/1000;
cout << tmp1 << " " << tmp2 << " " << to_string(tmp2) << endl;

Ottieni

99463 99.463 99.462997

Nota: la variabile num può essere qualsiasi valore vicino a 99.463, otterrai la stessa stampa. Il punto è evitare la comoda funzione c ++ 11 "to_string". Mi ci è voluto un po 'per uscire da questa trappola. Il modo migliore sono i metodi stringstream e sprintf (linguaggio C). C ++ 11 o più recente dovrebbe fornire un secondo parametro come numero di cifre dopo il punto mobile da mostrare. In questo momento il valore predefinito è 6. Sto postulando questo in modo che altri non perdano tempo su questo argomento.

Ho scritto la mia prima versione, per favore fammi sapere se trovi qualche bug che deve essere corretto. Puoi controllare il comportamento esatto con iomanipulator. La mia funzione è mostrare il numero di cifre dopo il punto decimale.

string ftos(float f, int nd) {
   ostringstream ostr;
   int tens = stoi("1" + string(nd, '0'));
   ostr << round(f*tens)/tens;
   return ostr.str();
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.