Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

écriture des nombres (formatage de sortie)

32 réponses
Avatar
KooK
Bonjour,

Je voudrais écrire les nombres avec un espace tous les 3 chiffres afin
de les rendre plus lisibles. J'ai regardé du coté de setf et setw sans
succès. Suis-je passé à côté ? Quel fonction utiliser ?

exemple :
int main(void)
{
int a = 1000000;
//fonction pour faire afficher 1 000 000
cout << a << endl;
return 0;
}

Si vous savez répondre à la même question en C je suis preneur aussi.

KooK

10 réponses

1 2 3 4
Avatar
Olivier Miakinen

Je voudrais écrire les nombres avec un espace tous les 3 chiffres afin
de les rendre plus lisibles. J'ai regardé du coté de setf et setw sans
succès. Suis-je passé à côté ? Quel fonction utiliser ?


J'ai bien ça en PHP, mais bon, je ne suis pas sûr que ça t'intéresse...

exemple :
int main(void)
{
int a = 1000000;
//fonction pour faire afficher 1 000 000
cout << a << endl;
return 0;
}

Si vous savez répondre à la même question en C je suis preneur aussi.


J'ai relu toute la doc de printf() et fonctions assimilées sans rien
trouver. Je suppose que tu dois pouvoir t'en sortir avec une fonction
qui transforme le nombre en chaîne de caractères (par sprintf() par
exemple) avant de découper le résultat en tranches.

--
Olivier Miakinen
Troll du plus sage chez les conviviaux : le nouveau venu, avec
son clan, s'infiltre dans les groupes de nouvelles. (3 c.)

Avatar
Sylvain
KooK wrote on 04/07/2006 16:40:
Bonjour,

Je voudrais écrire les nombres avec un espace tous les 3 chiffres afin
de les rendre plus lisibles. J'ai regardé du coté de setf et setw sans
succès. Suis-je passé à côté ? Quel fonction utiliser ?


pourquoi pas:

ostream& format3(ostream& out, long value)
{
if (value < 1000)
out << value;
else {
register int digit = (int) log10(value);
int divider = (long) pow(10.0, digit - (digit % 3));

long temp = (value / divider) % 1000;
char buff[5];
sprintf(buff, "%i", temp);
out << buff;
for (divider /= 1000; divider > 0; divider /= 1000){
temp = (value / divider) % 1000;
sprintf(buff, " %.03i", temp);
out << buff;
}
}
return out;
}

Si vous savez répondre à la même question en C je suis preneur aussi.


ça doit marcher aussi.

Sylvain.

Avatar
Arnaud Debaene
"KooK" a écrit dans le message de news:
44aa7d2d$0$31511$
Bonjour,

Je voudrais écrire les nombres avec un espace tous les 3 chiffres afin de
les rendre plus lisibles. J'ai regardé du coté de setf et setw sans
succès. Suis-je passé à côté ? Quel fonction utiliser ?

exemple :
int main(void)
{
int a = 1000000;
//fonction pour faire afficher 1 000 000
cout << a << endl;
return 0;
}



Il faut utiliser les locales, plus particulièrement le facet numpunct :

class MyNumPunct : public std::numpunct<char>
{
protected:
virtual std::string do_grouping() const
{
return "3"; //groupes de 3 chiffres. Oui, le formattage de cetet
chaine est stupide.....
}

virtual char do_thousands_sep() const
{
return ' '; //séparés par espace
}
};

int main()
{
MyNumPunct* punct=new MyNumPunct(); //will be destroyed by the locale
destructor

//create a new locale based on cout, but using our specific numpunct
std::locale loc(std::cout.getloc (), punct);

std::cout.imbue (loc);
std::cout<<2123456789<<std::endl;
}

cf
http://www.roguewave.com/support/docs/leif/sourcepro/html/stdlibref/num-put.html
pour plus de détails.

Arnaud
MVP - VC

Avatar
kanze
Sylvain wrote:
KooK wrote on 04/07/2006 16:40:
Bonjour,

Je voudrais écrire les nombres avec un espace tous les 3 chiffres afin
de les rendre plus lisibles. J'ai regardé du coté de setf et setw s ans
succès. Suis-je passé à côté ? Quel fonction utiliser ?


pourquoi pas:

ostream& format3(ostream& out, long value)
{
if (value < 1000)
out << value;
else {
register int digit = (int) log10(value);
int divider = (long) pow(10.0, digit - (digit % 3));

long temp = (value / divider) % 1000;
char buff[5];
sprintf(buff, "%i", temp);
out << buff;
for (divider /= 1000; divider > 0; divider /= 1000){
temp = (value / divider) % 1000;
sprintf(buff, " %.03i", temp);
out << buff;
}
}
return out;
}

Si vous savez répondre à la même question en C je suis preneur au ssi.


ça doit marcher aussi.


Pourquoi faire simple quand on peut faire compliqué, n'est-ce
pas ? Pour le C++, Arnaud a donné la bonne réponse. En C, c'est
moins évident, mais dans les deux cas, la première chose à
faire, c'est de voir s'il n'y a pas un locale tout fait qui fait
ce que tu veux. Sinon, en C++, on en crée un, comme a expliqué
Arnaud. En C, il faudrait voir au niveau de système comment en
installer un ; C n'offre pas de possibilités d'en créer un à
l'intérieur du programme. (Posix et Linux, par exemple,
fournissent une commande « localedef » pour le faire. En
revanche, en général il faut être root pour pouvoir le faire.)

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
adebaene


Pourquoi faire simple quand on peut faire compliqué, n'est-ce
pas ? Pour le C++, Arnaud a donné la bonne réponse.



J'ai un petit doute sur la réponse que j'ai donné magré tout :

int main()
{
MyNumPunct* punct=new MyNumPunct(); //will be destroyed by the
locale
destructor

//create a new locale based on cout, but using our specific numpunct

std::locale loc(std::cout.getloc (), punct);
....

Le fait que la locale appelle delete sur punct dans son destructeur, je
l'observe bien dans mon implémentation (VC2005), mais je n'ai pas
réussi à trouver de documentation "officielle" (ni dans le Josuttis,
ni dans la norme) qui spécifie clairement que c'est un comportement
normal, et comme je trouve que c'est une API un peu bizarre, je me
demande s'il n'y a pas un loup...

Ceci-dit, toute l'API des locale est "bizarre" ;-) Si quelqu'un a une
explication claire sur les règles de gestion des durées de vie des
facets par rapport aux locales, je suis preneur....

Arnaud
MVP - VC

Avatar
Franck Branjonneau
écrivait:

Le fait que la locale appelle delete sur punct dans son destructeur, je
l'observe bien dans mon implémentation (VC2005), mais je n'ai pas
réussi à trouver de documentation "officielle" (ni dans le Josuttis,
ni dans la norme) qui spécifie clairement que c'est un comportement
normal, et comme je trouve que c'est une API un peu bizarre, je me
demande s'il n'y a pas un loup...


22.1.1.2

Ceci-dit, toute l'API des locale est "bizarre" ;-) Si quelqu'un a une
explication claire sur les règles de gestion des durées de vie des
facets par rapport aux locales, je suis preneur....


22.1.1.2 ?
--
Franck Branjonneau

Avatar
Sylvain
kanze wrote on 05/07/2006 10:29:

Pourquoi faire simple quand on peut faire compliqué, n'est-ce pas?


tu as raison, j'aurais du écrire:

ostream& format3(ostream& out, long value){
register int digit = (int) log10(value);
long divider = (long) pow(10.0, digit - (digit % 3));
out << ((value / divider) % 1000);
for (divider /= 1000; divider > 0; divider /= 1000){
char buff[5];
sprintf(buff, " %.03i", (value / divider) % 1000);
out << buff;
}
return out;
}

(je laisse le "if (value<0){ out <<'-'; value = -value; }" à la
discrétion du lecteur).

Pour le C++, Arnaud a donné la bonne réponse. En C, c'est
moins évident,


en C++ comme en C, qu'est-ce que ostream et string peuvent utiliser
d'autre que sprintf ?? "ils" ont raison, coder un subset sur les mêmes
bases est mal ? intéressant ...

une chose plus intéressante aurait été de préférer une fonction
retournant un std::string afin de l'utiliser dans un chainage de <<
(tout en permettant le formatage à destination d'un string (basic_string
n'héritant pas de ostream) ou, indirectement, d'un ostream).

mais dans les deux cas, la première chose à
faire, c'est de voir s'il n'y a pas un locale tout fait


tout fait ou pas, mon locale à moi ((c) 1995 by P.J. Plauger, du
Platform SDK Microsoft 2005) ne contient aucun constructeur avec le
prototype (??, numpunct<?>*); je mets un "??" pour le
"std::cout.getLoc()" car je n'ai pas trouvé de std::cout non plus.

<hs> d'ailleurs je me demande si ce n'est pas un peu pour cela que je
préfère la complexité de mes fonctions de 8 lignes dignes d'une première
matinée d'initiation au C à la simplicité des include ""opaques"" de la
stl. </hs>

Sylvain.

Avatar
Arnaud Debaene
"Sylvain" a écrit dans le message de news:
44ac3c9c$0$872$

mais dans les deux cas, la première chose à
faire, c'est de voir s'il n'y a pas un locale tout fait


tout fait ou pas, mon locale à moi ((c) 1995 by P.J. Plauger, du Platform
SDK Microsoft 2005) ne contient aucun constructeur avec le prototype (??,
numpunct<?>*); je mets un "??" pour le "std::cout.getLoc()" car je n'ai
pas trouvé de std::cout non plus.


Ah? J'ai pourtant testé mon exemple avec Visual 2005 sans problèmes ;-)

Arnaud


Avatar
kanze
wrote:

Pourquoi faire simple quand on peut faire compliqué,
n'est-ce pas ? Pour le C++, Arnaud a donné la bonne réponse.


J'ai un petit doute sur la réponse que j'ai donné magré tout :

int main()
{
MyNumPunct* punct=new MyNumPunct(); //will be destroyed by the
locale
destructor

//create a new locale based on cout, but using our specific numpunct

std::locale loc(std::cout.getloc (), punct);
....

Le fait que la locale appelle delete sur punct dans son
destructeur, je l'observe bien dans mon implémentation
(VC2005), mais je n'ai pas réussi à trouver de documentation
"officielle" (ni dans le Josuttis, ni dans la norme) qui
spécifie clairement que c'est un comportement normal, et comme
je trouve que c'est une API un peu bizarre, je me demande s'il
n'y a pas un loup...


Dans la norme, §22.1.1.1.2/2 :

The refs arubment to the constructor is used for lifetime
management.

-- For refs == 0, the implementation performs delete
static_cast<locale::facet*>(f) (where f is a pointer to
the facet) when the last locale object containing the
facet is destroyed; for refs == 1, the implementation
never destroys the facet.

Ça, ça fait partie de la déscription de std::locale::facet.
Ensuite, il faut voir que std::numpunct<char> dont tu dérive
dérive de cette classe, qu'elle a aussi un refs paramètre,
qu'elle propage à sa classe de base, que ce paramètre a une
valeur par défaut de 0, et que ton code n'a pas spécifié autre
chose dans la liste d'initialisation.

Le code d'Arnaud était, je crois, à titre d'exemple ; il montre
aussi très bien comme on fait dans une application, au niveau
applicatif. Si on concevait en revanche une classe pour une
bibliothèque, avec beaucoup d'utilisateurs, on ajouterait
probablement aussi un paramètre refs, avec une valeur de défaut
de 0, qu'on propage à la classe de base.

Ce qui permettrait, évidemment, d'utiliser une instance statique
de MyNumPunct.

Ceci-dit, toute l'API des locale est "bizarre" ;-)


N'est-ce pas:-) ?

Si quelqu'un a une explication claire sur les règles de
gestion des durées de vie des facets par rapport aux locales,
je suis preneur....


Ce que j'ai cité ci-dessus.

Note bien que ce n'est pas sans danger. Si tu veux utiliser une
facette, il faut toujours s'assurer qu'au moins une copie du
locale dont tu l'as obtenu existe. Donc, par exemple, si tu veux
réaliser une comparaison de chaînes insensible à la case des
caractères, tu vas commencer intuitivement par écrire quelque
chose du genre :

class CharEq
{
typedef std::ctype< char >
CType ;
public:
explicit CharEq( std::locale const& l )
: myCType( &std::use_facet< CType >( l ) )
{
}

bool operator()( char lhs, char rhs ) const
{
return myCType->tolower( lhs ) == myCType->tolower( rhs ) ;
}
private:
// pointeur, plutôt que référence, afin de supporter
// l'affectation...
CType const* myCType ;
} ;

Et 99.9% des fois, ça marcherait sans problème. Seulement, tu n'as
aucune garantie -- et un jour ou l'autre, quelqu'un créera un
locale sur la volée, en tant que temporaire, et du coup, tu
risques d'utiliser un objet CType qui a déjà été détruit. La
solution, évident, c'est d'ajouter un membre de type std::locale
à la classe, que tu initialises avec un copie du paramètre du
constructeur, et qui donc assure la pérenité de la facette.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
Sylvain wrote:
kanze wrote on 05/07/2006 10:29:

Pourquoi faire simple quand on peut faire compliqué, n'est-ce pas?


tu as raison, j'aurais du écrire:

ostream& format3(ostream& out, long value){
register int digit = (int) log10(value);
long divider = (long) pow(10.0, digit - (digit % 3));
out << ((value / divider) % 1000);
for (divider /= 1000; divider > 0; divider /= 1000){
char buff[5];
sprintf(buff, " %.03i", (value / divider) % 1000);
out << buff;
}
return out;
}


Non plus. Pourquoi toutes ces manipulations numériques ?
N'oublie pas non plus qu'il va falloir implémenter la même chose
pour les flottants, et là, les manipulations numériques vont
générer même des erreurs. C'est carrément incompréhensible, et
il fait beaucoup de calculs supplémentaires pour rien.

(je laisse le "if (value<0){ out <<'-'; value = -value; }" à
la discrétion du lecteur).

Pour le C++, Arnaud a donné la bonne réponse. En C, c'est
moins évident,


en C++ comme en C, qu'est-ce que ostream et string peuvent utiliser
d'autre que sprintf ??


En C++, ostream ne peut pas très bien utiliser sprintf, parce
que la norme exige qu'il utilise std::num_put (qui doit utiliser
la facette std::numpunct du locale de ios_base qu'il reçoit en
paramètre -- et j'avoue que l'interface est un peu tordue).

En fait, même en C, on préfère snprintf à sprintf, pour des
raisons évidentes. Mais rien ne dit que snprintf n'appelle pas
std::ostringstream.

Mais ce n'est pas réelement l'utilisation de sprintf ici qui me
gène le plus. C'est tout ces calculs inutiles et obfuscants. Si
on rejette ostringstream pour une raison quelconque, et qu'on
préfère sprintf, malgré ces problèmes, je pourrais bien voir une
solution qui commençait à peu près :

char buffer[ sizeof( long ) * CHAR_BIT ] ;
sprintf( buffer, "%d", value ) ;
// ...

Ensuite, on insère les espaces où voulu.

"ils" ont raison, coder un subset sur les mêmes bases est mal
? intéressant ...


Je ne comprends pas cette phrase. Quel sous-ensemble ? De
quoi ? Sur quelles bases ?

une chose plus intéressante aurait été de préférer une
fonction retournant un std::string afin de l'utiliser dans un
chainage de << (tout en permettant le formatage à destination
d'un string (basic_string n'héritant pas de ostream) ou,
indirectement, d'un ostream).


C'est effectivement une altérnative intéressante. Quand j'ai
implémenté Gabi::Format (ou GB_Format, comme il s'appelait
alors), je me suis bien arrangé pour qu'il supporte une
conversion en std::string (ou en GB_String, dans les premières
versions, qui ont précédé la norme) aussi bien que l'opérateur
<<. (Pour ceux que ça intéresse, le code de Gabi::Format est
encore disponible à
http://kanze.james.neuf.fr/code/Old/Text/Format/index.html. Mais
ça fait un moment que je ne le maintiens plus -- dans la
pratique, le formattage à la printf est tout sauf intuitif. En
revanche, les dernières mises-à-jour datent d'une époque où je
ne pouvais pas encore compter sur un support des locales C++. Du
coup, j'ai implémenté ma propre internationalisation. Y compris
le gestion des séparateurs des milliers.)

mais dans les deux cas, la première chose à faire, c'est de
voir s'il n'y a pas un locale tout fait


tout fait ou pas, mon locale à moi ((c) 1995 by P.J. Plauger,
du Platform SDK Microsoft 2005) ne contient aucun constructeur
avec le prototype (??, numpunct<?>*); je mets un "??" pour le
"std::cout.getLoc()" car je n'ai pas trouvé de std::cout non
plus.


Tu n'as pas dû bien régarder, parce que le code d'Arnaud marche
bien sur ma copie de VC++ 2005. Le contraire m'aurait d'ailleurs
étonné ; Dinkumware est probablement celui qui supporte le plus
en questions de std::locale. Je ne crois pas qu'il soit livré
avec le compilateur Microsoft, mais je sais qu'ils ont des
locales tout faits qui supporte ce qui a été démandé.

<hs> d'ailleurs je me demande si ce n'est pas un peu pour cela
que je préfère la complexité de mes fonctions de 8 lignes
dignes d'une première matinée d'initiation au C à la
simplicité des include ""opaques"" de la stl. </hs>


On a déjà compris ici. Tu n'as pas envie d'apprendre de
nouvelles techniques. Quoiqu'elles puissent apporter.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


1 2 3 4