Convertir float en chaîne avec la précision et le nombre de chiffres décimaux spécifiés?

88

Comment convertir un flottant en chaîne en C ++ tout en spécifiant la précision et le nombre de chiffres décimaux?

Par exemple: 3.14159265359 -> "3.14"

Shannon Matthews
la source
pourquoi ne pas créer un flottant temporaire avec la précision spécifiée que vous souhaitez, puis le convertir en chaîne?
Gilad
@Gilad Le 'flotteur temporaire' n'a pas de 'précision spécifiée' et il y a des cas où une telle approche échouera. Cette question revient essentiellement à demander "quel est l'équivalent du format '% .2f'"?
user2864740
1
Voir stackoverflow.com/questions/14432043/c-float-formatting si ce n'est que besoin d'E / S.
user2864740

Réponses:

131

Une manière typique serait d'utiliser stringstream:

#include <iomanip>
#include <sstream>

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

Voir fixe

Utiliser la notation à virgule flottante fixe

Définit le floatfield indicateur de format pour le flux strfixed .

Lorsque floatfieldest défini sur fixed, les valeurs à virgule flottante sont écrites en utilisant la notation à virgule fixe: la valeur est représentée avec exactement autant de chiffres dans la partie décimale que spécifié par le champ de précision ( precision) et sans partie d'exposant.

et définir la précision .


Pour conversions à des fins techniques , comme le stockage de données dans un fichier XML ou JSON, C ++ 17 définit la famille de fonctions to_chars .

En supposant un compilateur conforme (qui nous manque au moment de la rédaction), quelque chose comme ça peut être envisagé:

#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
}
AlexD
la source
28

La méthode habituelle pour faire ce genre de chose est d '«imprimer dans une chaîne». En C ++, cela signifie utiliser std::stringstreamquelque chose comme:

std::stringstream ss;
ss << std::fixed << std::setprecision(2) << number;
std::string mystring = ss.str();
Mats Petersson
la source
12

Une autre option est snprintf:

double pi = 3.1415926;

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

Démo . La gestion des erreurs doit être ajoutée, c'est-à-dire la vérificationwritten < 0.

Columbo
la source
Pourquoi serait snprintfmieux dans ce cas? Veuillez expliquer ... Ce ne sera certainement pas plus rapide, produisez moins de code [J'ai écrit une implémentation printf, c'est assez compact avec un peu moins de 2000 lignes de code, mais avec des extensions de macro, cela atteint environ 2500 lignes]
Mats Petersson
1
@MatsPetersson Je crois fermement que ce sera plus rapide. C'est la raison pour laquelle j'ai fait cette réponse en premier lieu.
Columbo
@MatsPetersson J'ai effectué des mesures (GCC 4.8.1, -O2) qui indiquent que snprintf prend en effet moins de temps, d'un facteur 17/22. Je téléchargerai le code sous peu.
Columbo
@MatsPetersson Mon benchmark est probablement défectueux, mais la version stringstream prend deux fois plus de temps que la version snprintf à 1000000 itérations (Clang 3.7.0, libstdc ++, -O2).
J'ai aussi fait quelques mesures - et il semblerait (au moins sous Linux) que stringstream et snprintf utilisent les mêmes __printf_fpfonctionnalités sous-jacentes - il est plutôt étrange que cela prenne tellement plus de temps stringstream, car cela ne devrait vraiment pas avoir à le faire.
Mats Petersson
9

Vous pouvez utiliser la fmt::formatfonction de la bibliothèque {fmt} :

#include <fmt/core.h>

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

2est une précision.

Cette fonction de formatage a été proposée pour la normalisation en C ++: P0645 . P0645 et {fmt} utilisent tous deux une syntaxe de chaîne de format semblable à Python qui est similaire à celle de printf's mais qui utilise {}comme délimiteurs au lieu de %.

vitaut
la source
7

Voici une solution utilisant uniquement std. Cependant, notez que cela arrondit seulement vers le bas.

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

Car roundedcela donne:

3.14

Le code convertit tout le flottant en chaîne, mais coupe tous les caractères 2 caractères après le "."

Sébastien R.
la source
La seule chose est que de cette façon, vous n’arrondissez pas réellement le nombre.
ChrCury78
1
@ ChrCury78 comme indiqué dans ma réponse, cela ne fait qu'arrondir. Si vous voulez un arrondi normal, ajoutez simplement 5 au diget suivant votre valeur. Par exemple number + 0.005dans mon cas.
Sebastian R.
2

Ici, je donne un exemple négatif où vous voulez éviter lors de la conversion de nombres flottants en chaînes.

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

Vous obtenez

99463 99.463 99.462997

Remarque: la variable num peut être n'importe quelle valeur proche de 99,463, vous obtiendrez la même impression. Le but est d'éviter la fonction pratique "to_string" de c ++ 11. Il m'a fallu un certain temps pour sortir ce piège. Le meilleur moyen est les méthodes stringstream et sprintf (langage C). C ++ 11 ou plus récent doit fournir un deuxième paramètre comme nombre de chiffres après la virgule flottante à afficher. Pour le moment, la valeur par défaut est 6. Je pose ceci pour que d'autres ne perdent pas de temps sur ce sujet.

J'ai écrit ma première version, faites-le moi savoir si vous trouvez un bogue qui doit être corrigé. Vous pouvez contrôler le comportement exact avec l'iomanipulator. Ma fonction est d'afficher le nombre de chiffres après la virgule décimale.

string ftos(float f, int nd) {
   ostringstream ostr;
   int tens = stoi("1" + string(nd, '0'));
   ostr << round(f*tens)/tens;
   return ostr.str();
}
Kemin Zhou
la source