IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++Builder Discussion :

convertir un chiffre en lettre


Sujet :

C++Builder

  1. #1
    Membre régulier
    Inscrit en
    Avril 2008
    Messages
    335
    Détails du profil
    Informations forums :
    Inscription : Avril 2008
    Messages : 335
    Points : 93
    Points
    93
    Par défaut convertir un chiffre en lettre
    pour convertir un chiffre en lettre j'ai utilisée ce code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    float Entier;
    Entier = StrToFloat(Label2->Caption);
    char lettre[256]="";
    unsigned int centaine, dizaine, unite, reste, y;
    bool dix = false;
    reste = Entier;
    for(int i=1000000000; i>=1; i/=1000)
    {
            y = reste/i;
            if(y!=0)
     
            {
     
            centaine = y/100;
            dizaine  = (y - centaine*100)/10;
            unite = y-(centaine*100)-(dizaine*10);
     
           switch(centaine)
            {
                    case 0:
                        break;
                    case 1:
                        StrCat(lettre,"Cent ");
                        break;
                    case 2:
                        if((dizaine == 0)&&(unite == 0)) StrCat(lettre,"Deux Cents ");
                        else StrCat(lettre,"Deux Cent ");
                        break;
                    case 3:
                        if((dizaine == 0)&&(unite == 0)) StrCat(lettre,"Trois Cents ");
                        else StrCat(lettre,"Trois Cent ");
                        break;
                    case 4:
                        if((dizaine == 0)&&(unite == 0)) StrCat(lettre,"Quatre Cents ");
                        else StrCat(lettre,"Quatre Cent ");
                        break;
                    case 5:
                        if((dizaine == 0)&&(unite == 0)) StrCat(lettre,"Cinq cents ");
                        else StrCat(lettre,"Cinq Cent ");
                        break;
                    case 6:
                        if((dizaine == 0)&&(unite == 0)) StrCat(lettre,"Six Cents ");
                        else StrCat(lettre,"Six Cent ");
                        break;
                    case 7:
                        if((dizaine == 0)&&(unite == 0)) StrCat(lettre,"Sept cents ");
                        else StrCat(lettre,"Sept Cent ");
                        break;
                    case 8:
                        if((dizaine == 0)&&(unite == 0)) StrCat(lettre,"Huit Cents ");
                        else StrCat(lettre,"Huit Cent ");
                        break;
                    case 9:
                        if((dizaine == 0)&&(unite == 0)) StrCat(lettre,"Neuf Cents ");
                        else StrCat(lettre,"Neuf Cent ");
                }
                switch(dizaine)
                {
                    case 0:
                        break;
                    case 1:
                        dix = true;
                        break;
                    case 2:
                        StrCat(lettre,"Vingt ");
                        break;
                    case 3:
                        StrCat(lettre,"Trente ");
                        break;
                    case 4:
                        StrCat(lettre,"Quarante ");
                        break;
                    case 5:
                        StrCat(lettre,"Cinquante ");
                        break;
                    case 6:
                        StrCat(lettre,"Soixante ");
                        break;
                    case 7:
                        dix = true;
                        StrCat(lettre,"Soixante ");
                        break;
                    case 8:
                        StrCat(lettre,"Quatre-Vingt ");
                        break;
                    case 9:
                        dix = true;
                        StrCat(lettre,"Quatre-Vingt ");
                } // endSwitch(dizaine)
                switch(unite)
                {
                    case 0:
                        if(dix)
                        {
                        StrCat(lettre,"Dix ");
                        }
                        break;
                    case 1:
                        if(dix)
                        {
                        StrCat(lettre,"Onze ");
                        }
                        else
                        {
                        StrCat(lettre,"Un ");
                        }
                        break;
                    case 2:
                        if(dix)
                        {
                        StrCat(lettre,"Douze ");
                        }
                        else
                        {
                        StrCat(lettre,"Deux ");
                        }
                        break;
                    case 3:
                        if(dix)
                        {
                        StrCat(lettre,"Treize ");
                        }
                        else
                        {
                        StrCat(lettre,"Trois ");
                        }
                        break;
                    case 4:
                        if(dix)
                        {
                        StrCat(lettre,"Quatorze ");
                        }
                        else
                        {
                        StrCat(lettre,"quatre ");
                        }
                        break;
                    case 5:
                        if(dix) StrCat(lettre,"Quinze ");
                        else    StrCat(lettre,"Cinq ");
                        break;
                    case 6:
                        if(dix) StrCat(lettre,"Seize ");
                        else    StrCat(lettre,"Six ");
                        break;
                    case 7:
                        if(dix) StrCat(lettre,"Dix-sept ");
                        else    StrCat(lettre,"Sept ");
                        break;
                    case 8:
                        if(dix) StrCat(lettre,"Dix-huit ");
                        else    StrCat(lettre,"Huit ");
                        break;
                    case 9:
                        if(dix) StrCat(lettre,"Dix-neuf ");
                        else    StrCat(lettre,"Neuf ");
                } 
                switch (i)
                {
                    case 1000000000:
                        if(y>1) StrCat(lettre,"Milliards ");
                        else StrCat(lettre,"Milliard ");
                        break;
                    case 1000000:
                        if(y>1) StrCat(lettre,"Millions ");
                        else StrCat(lettre,"Million ");
                        break;
                    case 1000:
                        StrCat(lettre,"Mille ");
                }
            } // end if(y!=0)
            reste -= y*i;
            dix = false;
        } // end for
    if(strlen(lettre)==0) strcpy(lettre,"Zero");
     
    Label1->Caption=lettre;
    ce code fonctionne tres bien sauf dans le cas par ex 1945 résultat donne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Un mille neuf cent quarante cinq
    je veut sans Un
    merci d'avance pour votre aide

  2. #2
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 648
    Points
    7 648
    Par défaut
    Bonjour,

    Si on est dans les "mille", on ne doit pas écrire le "un" s'il est seul. Ça devient ligne 105
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if ( i!=1000 || centaine || dizaine ) StrCat(lettre,"Un ");
    Et on ne dit pas "Vingt Un" mais "Vingt-et-un", il manque donc aussi autre chose.

  3. #3
    Membre régulier
    Inscrit en
    Avril 2008
    Messages
    335
    Détails du profil
    Informations forums :
    Inscription : Avril 2008
    Messages : 335
    Points : 93
    Points
    93
    Par défaut
    Merciiiiiiiiiiiiii beaucoup dalfab.

    vous avez raison concernant cette remarque
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Et on ne dit pas "Vingt Un" mais "Vingt-et-un"
    vous pouvez me dirigé pour résoudre ce probleme.
    merci encore une autre fois.

  4. #4
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 648
    Points
    7 648
    Par défaut
    Si on oublie les tirets de la nouvelle orthographe (qui sont manquant partout) et qui amènerait une grosse modification sur la gestion des espaces. Ce que l'on veut c'est écrire "et un" au lieu de "un" quand la dizaine est comprise entre 2 et 6, ça n'est pas si difficile.

  5. #5
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 471
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 471
    Points : 6 110
    Points
    6 110
    Par défaut
    Bonjour,

    Il faudrait transformer le pâté de code en programme structuré.

    Dans ton code, tu récupères une chaîne dans un composant graphique Label2, puis tu la convertie en entier, puis tu as un gros code qui convertit cet entier en une chaîne que tu mets dans un autre composant graphique Label1. Le gros bout de code en question devrait être isolé dans une fonction.

    Exemple : fichier "FrenchUtility.h" :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    #ifndef INCLUDE__MY_PROJECT__FRENCH_UTILITY__H
    #define INCLUDE__MY_PROJECT__FRENCH_UTILITY__H
     
    #include <string>
     
    namespace MyProject::FrenchUtility {
     
    	std::string convertToFrenchUtf8String(int number);
     
    } // namespace MyProject::FrenchUtility
     
    #endif
    Cette fonction peut elle-même être décomposée en sous-fonctions.

    Exemple : fichier "FrenchUtility.cpp" :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    #include "FrenchUtility.h"
     
    #include <cstdint>
    #include <limits>
    #include <string>
     
    namespace MyProject {
     
    namespace {
    	constexpr std::intmax_t cMAX_INTEGER_FOR_FRENCH_STRING = INTMAX_C(999'999'999'999);
    	constexpr std::intmax_t cMIN_INTEGER_FOR_FRENCH_STRING = -cMAX_INTEGER_FOR_FRENCH_STRING;
     
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Wtype-limits"
    	static_assert(std::numeric_limits<int>::max() <= cMAX_INTEGER_FOR_FRENCH_STRING);
    	static_assert(std::numeric_limits<int>::min() >= cMIN_INTEGER_FOR_FRENCH_STRING);
    #pragma GCC diagnostic pop
     
    	std::string convertToFrench(unsigned number);
    	// D'autres fonctions déclarées en avance...
    }
     
    std::string FrenchUtility::convertToFrenchUtf8String(int number)
    {
    	const std::string prefix = (number < 0) ? u8"moins " : u8"";
    	const auto positiveNumber = static_cast<unsigned>((number < 0) ? -number : number);
    	return prefix + convertToFrench(positiveNumber);
    }
     
    namespace
    {
    	std::string convertToFrench(unsigned number)
    	{
    		// Du code...
    	}
     
    	// Les définitions des autres fonctions...
    }
     
    } // namespace MyProject
    Ensuite, pour tester ce genre de fonction, le mieux, c'est de faire des tests unitaires.

    Exemple avec Boost.Test sur GCC :

    main.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #define BOOST_TEST_MODULE Unit tests of MyProject
     
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Weffc++"
    #pragma GCC diagnostic ignored "-Wmissing-declarations"
    #pragma GCC diagnostic ignored "-Wold-style-cast"
    #pragma GCC diagnostic ignored "-Woverloaded-virtual"
    #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
    # include <boost/test/unit_test.hpp>
    #pragma GCC diagnostic pop
    FrenchUtilityTest.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Weffc++"
    #pragma GCC diagnostic ignored "-Wold-style-cast"
    #pragma GCC diagnostic ignored "-Woverloaded-virtual"
    #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
    # include <boost/test/unit_test.hpp>
    #pragma GCC diagnostic pop
     
    #include "MyProject/FrenchUtility.h"
     
    namespace MPFU = MyProject::FrenchUtility;
     
    BOOST_AUTO_TEST_SUITE(FrenchUtilityTest_TheTestSuite)
     
    BOOST_AUTO_TEST_CASE(SomeTest)
    {
    	BOOST_TEST(MPFU::convertToFrenchUtf8String(0)   == u8"zéro");
    	BOOST_TEST(MPFU::convertToFrenchUtf8String(1)   == u8"un");
    	BOOST_TEST(MPFU::convertToFrenchUtf8String(-1)  == u8"moins un");
    	BOOST_TEST(MPFU::convertToFrenchUtf8String(11)  == u8"onze");
    	BOOST_TEST(MPFU::convertToFrenchUtf8String(-11) == u8"moins onze");
    }
     
    BOOST_AUTO_TEST_SUITE_END()
    Remarques spécifiques à C++ Builder :
    • Il faudra enlever mes #pragma GCC, car C++ Builder n'en tiendra pas compte.
    • Comme Embarcadero est en retard sur le C++, il y aura probablement quelques adaptations à faire dans le code pour que ça compile.
    • A la place de Boost.Test, tu peux utiliser le framework de tests unitaires de Embarcadero.

  6. #6
    Membre régulier
    Inscrit en
    Avril 2008
    Messages
    335
    Détails du profil
    Informations forums :
    Inscription : Avril 2008
    Messages : 335
    Points : 93
    Points
    93
    Par défaut
    Salut
    voici le code que j'ai utilisée
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    case 1:
                        if(dix)
                        {
                        StrCat(lettre,"Onze ");
                        }
                        else
                        {
                      if ( i!=1000 || centaine || dizaine  )
                         {
                           if(dizaine ==0 )
                              {
                                   StrCat(lettre,"Un ");
                               }
                                 else
                                 {  if ( ((dizaine >=2) && (dizaine <=6)||(dizaine ==8)) )
                                     {
                                      StrCat(lettre,"-et-Un ");
                                      }
                                 }
     
                          }
                         }
                        break;

  7. #7
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 648
    Points
    7 648
    Par défaut
    qu'est devenu "quatre-vingt-un" ?

  8. #8
    Membre régulier
    Inscrit en
    Avril 2008
    Messages
    335
    Détails du profil
    Informations forums :
    Inscription : Avril 2008
    Messages : 335
    Points : 93
    Points
    93
    Par défaut
    Bonjour merci dalfab. voici mon nouveau code après votre remarque

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    case 1:
                        if(dix)
                        {
                        StrCat(lettre,"Onze ");
                        }
                        else
                        {
                      if ( i!=1000 || centaine || dizaine  )
                         {
                                if  ((dizaine >=2) && (dizaine <=6) )
                                     {
                                      StrCat(lettre,"-et-Un ");
                                      }
                                           else {
                                                  StrCat(lettre,"Un ");
     
                                                 }
                         }
     
                         }

  9. #9
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 471
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 471
    Points : 6 110
    Points
    6 110
    Par défaut
    Il faut aussi prendre en compte les cas où on écrit "quatre-vingts" au lieu de "quatre-vingt".

    Cette fois-ci, je vais publier une correction :

    FrenchUtility.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #ifndef INCLUDE__PYRAMIDEV__FRENCH_UTILITY__H
    #define INCLUDE__PYRAMIDEV__FRENCH_UTILITY__H
     
    #include <string>
     
    namespace Pyramidev::FrenchUtility {
     
    	// Remarque : En français, on écrit "quatre-vingts pages" et "huit-cents ans",
    	// mais on écrit "la page quatre-vingt" et "l'an huit-cent".
    	// Plus de détails ici :
    	// http://parler-francais.eklablog.com/accord-de-vingt-cent-et-mille-a3877321
     
    	//! Exemples :
    	//! - Si nombre == 80'080'080 et plurielSiPossible == true, alors on retourne
    	//!   u8"quatre-vingts millions quatre-vingt-mille-quatre-vingts".
    	//! - Si nombre == 80'080'080 et plurielSiPossible == false, alors on retourne
    	//!   u8"quatre-vingts millions quatre-vingt-mille-quatre-vingt".
    	//! - Si nombre == 800'800'800 et plurielSiPossible == true, alors on retourne
    	//!   u8"huit-cents millions huit-cent-mille-huit-cents".
    	//! - Si nombre == 800'800'800 et plurielSiPossible == false, alors on retourne
    	//!   u8"huit-cents millions huit-cent-mille-huit-cent".
    	//! - Si nombre == -1'001'001, alors on retourne u8"moins un million mille-un".
    	std::string convertirNombreEnChaineFrancaiseUtf8(int      nombre, bool plurielSiPossible = true);
    	std::string convertirNombreEnChaineFrancaiseUtf8(unsigned nombre, bool plurielSiPossible = true);
     
    } // namespace Pyramidev::FrenchUtility
     
    #endif
    FrenchUtility.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    #include "Pyramidev/FrenchUtility.h"
     
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Weffc++"
    #pragma GCC diagnostic ignored "-Wold-style-cast"
    # include <boost/algorithm/string/join.hpp>
    #pragma GCC diagnostic pop
     
    #include <limits>
    #include <stdexcept>
    #include <string>
    #include <type_traits>
    #include <vector>
     
    namespace Pyramidev {
     
    namespace {
    	constexpr unsigned cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE = 3;
     
    	template<class TypeEntier>
    	constexpr TypeEntier partieEntiereLog1000(TypeEntier nombrePositif)
    	{
    		static_assert(std::numeric_limits<TypeEntier>::is_integer);
    		if(nombrePositif < 0)
    			throw std::logic_error(u8"nombre strictement négatif");
     
    		TypeEntier resultat = 0;
    		while(nombrePositif >= 1000) {
    			nombrePositif /= 1000;
    			++resultat;
    		}
    		return resultat;
    	}
     
    	template<class TypeNombre, class TypeEntier>
    	constexpr TypeNombre puissance(TypeNombre nombre, TypeEntier exposantPositif)
    	{
    		static_assert(std::is_integral_v<TypeNombre>);
    		static_assert(std::numeric_limits<TypeEntier>::is_integer);
    		if(exposantPositif < 0)
    			throw std::logic_error(u8"nombre strictement négatif");
    		if(nombre == 0 && exposantPositif == 0)
    			throw std::logic_error(u8"0 puissance 0 n'est pas défini.");
     
    		TypeNombre resultat = 1;
    		for(TypeEntier k = 0; k < exposantPositif; ++k)
    			resultat *= nombre;
    		return resultat;
    	}
     
    	std::string chaineNombreEntre1Et999Fois1000Puissance(unsigned nombre, unsigned exposant,
    	                                                     bool plurielSiPossible);
    	std::string chaineNombreEntre1Et999(unsigned nombre, bool plurielSiPossible);
    	std::string chaineNombreEntre1Et99(unsigned nombre, bool plurielSiPossible);
    	constexpr const char* motDe1000Puissance(unsigned exposant);
    	constexpr const char* motDe10FoisFacteurEntre1Et9(unsigned facteur);
    	constexpr const char* chaineNombreEntre1Et19(unsigned nombre);
    	constexpr const char* motDeChiffre(unsigned chiffre);
    }
     
    std::string FrenchUtility::convertirNombreEnChaineFrancaiseUtf8(int nombre, bool plurielSiPossible)
    {
    	const std::string prefixe = (nombre < 0) ? u8"moins " : u8"";
    	const auto nombrePositif = static_cast<unsigned>((nombre < 0) ? -nombre : nombre);
    	return prefixe + convertirNombreEnChaineFrancaiseUtf8(nombrePositif, plurielSiPossible);
    }
     
    std::string FrenchUtility::convertirNombreEnChaineFrancaiseUtf8(unsigned nombre, bool plurielSiPossible)
    {
    	if(nombre == 0)
    		return motDeChiffre(0);
     
    	std::vector<std::string> tripletsDeChiffres{};
     
    	const unsigned exposantMaximal = partieEntiereLog1000(nombre);
    		// Ex : exposantMaximal == 2 ssi nombre >= 1'000'000 && nombre < 1'000'000'000
     
    	{
    		constexpr unsigned exposantMaximalMaximal =
    			partieEntiereLog1000(std::numeric_limits<unsigned>::max());
    		static_assert(exposantMaximalMaximal <= cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE);
    			// Pour assurer la précondition de chaineNombreEntre1Et999Fois1000Puissance.
    	}
     
    	tripletsDeChiffres.reserve(exposantMaximal + 1);
     
    	for(int exposantSigne = exposantMaximal; exposantSigne >= 0; --exposantSigne)
    	{
    		const auto exposant = static_cast<unsigned>(exposantSigne);
     
    		const unsigned centainesDizainesEtUnites =
    			(nombre / puissance(1000, exposant)) % 1000;
     
    		// Exemple avec nombre == 12'003'450 :
    		// - Si exposant == 2, alors centainesDizainesEtUnites == 12
    		//   et on ajoute u8"douze millions" dans le vecteur tripletsDeChiffres.
    		// - Si exposant == 1, alors centainesDizainesEtUnites == 3
    		//   et on ajoute u8"trois-mille" dans le vecteur tripletsDeChiffres.
    		// - Si exposant == 0, alors centainesDizainesEtUnites == 450
    		//   et on ajoute u8"quatre-cent-cinquante" dans le vecteur tripletsDeChiffres.
     
    		if(centainesDizainesEtUnites > 0) {
    			tripletsDeChiffres.push_back(chaineNombreEntre1Et999Fois1000Puissance(
    				centainesDizainesEtUnites, exposant, plurielSiPossible
    			));
    		} else {
    			tripletsDeChiffres.push_back(u8"");
    		}
    	}
     
    	const size_t nbTripletsDeChiffres = tripletsDeChiffres.size();
    	assert(nbTripletsDeChiffres > 0);
     
    	// Récupérer la partie la plus grande.
    	std::string resultat = tripletsDeChiffres[0];
     
    	if(nombre > 1'000)
    	{
    		assert(nbTripletsDeChiffres >= 2);
    		const size_t indiceDesMilliers    = nbTripletsDeChiffres - 2;
    		const size_t indiceDernierePartie = nbTripletsDeChiffres - 1;
     
    		// Ajouter les éventuelles autres parties supérieures ou égales à mille.
    		// Exemple avec nombre == 12'003'450 :
    		// On ajoute u8" trois-mille" dans le résultat.
    		for(size_t k = 1; k <= indiceDesMilliers; ++k) {
    			if(tripletsDeChiffres[k].empty() == false) {
    				resultat += u8" ";
    				resultat += tripletsDeChiffres[k];
    			}
    		}
     
    		// Ajouter l'éventuelle autre partie inférieure à mille.
    		// Exemple avec nombre == 12'003'450 :
    		// On ajoute u8"-quatre-cent-cinquante" dans le résultat.
    		if(tripletsDeChiffres[indiceDernierePartie].empty() == false)
    		{
    			const bool ajouterTiret = (tripletsDeChiffres[indiceDesMilliers].empty() == false);
    			resultat += ajouterTiret ? u8"-" : u8" ";
    			resultat += tripletsDeChiffres[indiceDernierePartie];
    		}
    	}
     
    	assert(!resultat.empty());
    	return resultat;
    }
     
    namespace
    {
    	//! Exemple avec nombre == 300 :
    	//! - Si exposant == 0, alors on retourne "trois-cents" ou "trois-cent".
    	//! - Si exposant == 1, alors on retourne "trois-cent-mille".
    	//! - Si exposant == 2, alors on retourne "trois-cents millions".
    	//!
    	std::string chaineNombreEntre1Et999Fois1000Puissance(unsigned nombre, unsigned exposant,
    	                                                     bool plurielSiPossible)
    	{
    		assert(nombre >= 1);
    		assert(nombre <= 999);
    		assert(exposant <= cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE);
    			// car précondition de motDe1000Puissance
    		std::string resultat{};
    		switch(exposant) {
    			case 0:
    				resultat = chaineNombreEntre1Et999(nombre, plurielSiPossible);
    				break;
    			case 1:
    				if(nombre > 1) {
    					resultat = chaineNombreEntre1Et999(nombre, false);
    					resultat += u8"-";
    				}
    				resultat += motDe1000Puissance(1);
    				break;
    			default:
    				resultat = chaineNombreEntre1Et999(nombre, true);
    				resultat += u8" ";
    				resultat += motDe1000Puissance(exposant);
    				if(nombre > 1)
    					resultat += u8"s";
    		}
    		assert(!resultat.empty());
    		return resultat;
    	}
     
    	std::string chaineNombreEntre1Et999(unsigned nombre, bool plurielSiPossible)
    	{
    		assert(nombre >= 1);
    		assert(nombre <= 999);
    		const unsigned centaines        = nombre / 100;
    		const unsigned dizainesEtUnites = nombre % 100;
    		std::vector<std::string> resultat{};
    		if(centaines > 1)
    			resultat.push_back(motDeChiffre(centaines));
    		if(centaines > 0) {
    			if(plurielSiPossible && centaines > 1 && dizainesEtUnites == 0)
    				resultat.push_back(u8"cents");
    			else
    				resultat.push_back(u8"cent");
    		}
    		if(dizainesEtUnites > 0)
    			resultat.push_back(chaineNombreEntre1Et99(dizainesEtUnites, plurielSiPossible));
    		assert(!resultat.empty());
    		return boost::algorithm::join(resultat, u8"-");
    	}
     
    	std::string chaineNombreEntre1Et99(unsigned nombre, bool plurielSiPossible)
    	{
    		assert(nombre >= 1);
    		assert(nombre <= 99);
    		const unsigned dizaines = nombre / 10;
    		const unsigned unites   = nombre % 10;
    		std::string resultat{};
    		switch(dizaines) {
    			case 0:
    			case 1:
    				resultat = chaineNombreEntre1Et19(nombre);
    				break;
    			case 2:
    			case 3:
    			case 4:
    			case 5:
    				resultat = motDe10FoisFacteurEntre1Et9(dizaines);
    				if(unites == 1)
    					resultat += u8"-et";
    				if(unites >= 1) {
    					resultat += u8"-";
    					resultat += motDeChiffre(unites);
    				}
    				break;
    			case 6:
    			case 7:
    				resultat = motDe10FoisFacteurEntre1Et9(6);
    				if(nombre % 20 == 1)
    					resultat += u8"-et";
    				if(nombre % 20 >= 1) {
    					resultat += u8"-";
    					resultat += chaineNombreEntre1Et19(nombre % 20);
    				}
    				break;
    			case 8:
    			case 9:
    				resultat = motDe10FoisFacteurEntre1Et9(8);
    				if(plurielSiPossible && nombre % 20 == 0)
    					resultat += u8"s";
    				if(nombre % 20 >= 1) {
    					resultat += u8"-";
    					resultat += chaineNombreEntre1Et19(nombre % 20);
    				}
    				break;
    			default:
    				assert(false);
    		}
    		assert(!resultat.empty());
    		return resultat;
    	}
     
    	constexpr const char* motDe1000Puissance(unsigned exposant)
    	{
    		assert(exposant >= 1);
    		assert(exposant <= cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE);
    		static_assert(3 >= cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE);
    		switch(exposant) {
    			case 1: return u8"mille";
    			case 2: return u8"million";
    			case 3: return u8"milliard";
    			default:
    				throw std::logic_error(u8"unreachable code");
    		}
    	}
     
    	constexpr const char* motDe10FoisFacteurEntre1Et9(unsigned facteur)
    	{
    		assert(facteur >= 1);
    		assert(facteur <= 9);
    		switch(facteur) {
    			case 1: return chaineNombreEntre1Et19(10);
    			case 2: return u8"vingt";
    			case 3: return u8"trente";
    			case 4: return u8"quarante";
    			case 5: return u8"cinquante";
    			case 6: return u8"soixante";
    			case 7: return u8"soixante-dix";
    			case 8: return u8"quatre-vingt";
    			case 9: return u8"quatre-vingt-dix";
    			default:
    				throw std::logic_error(u8"unreachable code");
    		}
    	}
     
    	constexpr const char* chaineNombreEntre1Et19(unsigned nombre)
    	{
    		assert(nombre >= 1);
    		assert(nombre <= 19);
    		if(nombre >= 1 && nombre <= 9)
    			return motDeChiffre(nombre);
    		switch(nombre) {
    			case 10: return u8"dix";
    			case 11: return u8"onze";
    			case 12: return u8"douze";
    			case 13: return u8"treize";
    			case 14: return u8"quatorze";
    			case 15: return u8"quinze";
    			case 16: return u8"seize";
    			case 17: return u8"dix-sept";
    			case 18: return u8"dix-huit";
    			case 19: return u8"dix-neuf";
    			default:
    				throw std::logic_error(u8"unreachable code");
    		}
    	}
     
    	constexpr const char* motDeChiffre(unsigned chiffre)
    	{
    		assert(chiffre <= 9);
    		switch(chiffre) {
    			case 0: return u8"zéro";
    			case 1: return u8"un";
    			case 2: return u8"deux";
    			case 3: return u8"trois";
    			case 4: return u8"quatre";
    			case 5: return u8"cinq";
    			case 6: return u8"six";
    			case 7: return u8"sept";
    			case 8: return u8"huit";
    			case 9: return u8"neuf";
    			default:
    				throw std::logic_error(u8"unreachable code");
    		}
    	}
    }
     
    } // namespace Pyramidev
    FrenchUtilityTest.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Weffc++"
    #pragma GCC diagnostic ignored "-Wmissing-declarations"
    #pragma GCC diagnostic ignored "-Wold-style-cast"
    #pragma GCC diagnostic ignored "-Woverloaded-virtual"
    #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
    # include <boost/test/unit_test.hpp>
    #pragma GCC diagnostic pop
     
    #include "Pyramidev/FrenchUtility.h"
     
    BOOST_AUTO_TEST_SUITE(FrenchUtilityTest_conversionsDeNombresEnChaines)
     
    BOOST_AUTO_TEST_CASE(Test_convertirNombreEnChaineFrancaiseUtf8)
    {
    	for(bool plurielSiPossible : {true, false})
    	{
    		BOOST_TEST_CONTEXT("plurielSiPossible == " << (plurielSiPossible ? "true" : "false"))
    		{
     
    			auto convert = [plurielSiPossible](int number) {
    				return Pyramidev::FrenchUtility::convertirNombreEnChaineFrancaiseUtf8(
    					number, plurielSiPossible);
    			};
     
    			BOOST_TEST(convert(-1) == u8"moins un");
    			BOOST_TEST(convert(0)  == u8"zéro");
    			BOOST_TEST(convert(1)  == u8"un");
    			BOOST_TEST(convert(2)  == u8"deux");
    			BOOST_TEST(convert(3)  == u8"trois");
    			BOOST_TEST(convert(4)  == u8"quatre");
    			BOOST_TEST(convert(5)  == u8"cinq");
    			BOOST_TEST(convert(6)  == u8"six");
    			BOOST_TEST(convert(7)  == u8"sept");
    			BOOST_TEST(convert(8)  == u8"huit");
    			BOOST_TEST(convert(9)  == u8"neuf");
    			BOOST_TEST(convert(10) == u8"dix");
    			BOOST_TEST(convert(11) == u8"onze");
    			BOOST_TEST(convert(12) == u8"douze");
    			BOOST_TEST(convert(13) == u8"treize");
    			BOOST_TEST(convert(14) == u8"quatorze");
    			BOOST_TEST(convert(15) == u8"quinze");
    			BOOST_TEST(convert(16) == u8"seize");
    			BOOST_TEST(convert(17) == u8"dix-sept");
    			BOOST_TEST(convert(18) == u8"dix-huit");
    			BOOST_TEST(convert(19) == u8"dix-neuf");
    			BOOST_TEST(convert(20) == u8"vingt");
    			BOOST_TEST(convert(21) == u8"vingt-et-un");
    			BOOST_TEST(convert(22) == u8"vingt-deux");
    			BOOST_TEST(convert(29) == u8"vingt-neuf");
    			BOOST_TEST(convert(30) == u8"trente");
    			BOOST_TEST(convert(31) == u8"trente-et-un");
    			BOOST_TEST(convert(32) == u8"trente-deux");
    			BOOST_TEST(convert(39) == u8"trente-neuf");
    			BOOST_TEST(convert(40) == u8"quarante");
    			BOOST_TEST(convert(41) == u8"quarante-et-un");
    			BOOST_TEST(convert(42) == u8"quarante-deux");
    			BOOST_TEST(convert(49) == u8"quarante-neuf");
    			BOOST_TEST(convert(50) == u8"cinquante");
    			BOOST_TEST(convert(51) == u8"cinquante-et-un");
    			BOOST_TEST(convert(52) == u8"cinquante-deux");
    			BOOST_TEST(convert(59) == u8"cinquante-neuf");
    			BOOST_TEST(convert(60) == u8"soixante");
    			BOOST_TEST(convert(61) == u8"soixante-et-un");
    			BOOST_TEST(convert(62) == u8"soixante-deux");
    			BOOST_TEST(convert(69) == u8"soixante-neuf");
    			BOOST_TEST(convert(70) == u8"soixante-dix");
    			BOOST_TEST(convert(71) == u8"soixante-onze");
    			BOOST_TEST(convert(72) == u8"soixante-douze");
    			BOOST_TEST(convert(79) == u8"soixante-dix-neuf");
    			if(plurielSiPossible)
    				BOOST_TEST(convert(80) == u8"quatre-vingts");
    			else
    				BOOST_TEST(convert(80) == u8"quatre-vingt");
    			BOOST_TEST(convert(81) == u8"quatre-vingt-un");
    			BOOST_TEST(convert(82) == u8"quatre-vingt-deux");
    			BOOST_TEST(convert(89) == u8"quatre-vingt-neuf");
    			BOOST_TEST(convert(90) == u8"quatre-vingt-dix");
    			BOOST_TEST(convert(91) == u8"quatre-vingt-onze");
    			BOOST_TEST(convert(92) == u8"quatre-vingt-douze");
    			BOOST_TEST(convert(99) == u8"quatre-vingt-dix-neuf");
     
    			BOOST_TEST(convert(100) == u8"cent");
    			BOOST_TEST(convert(101) == u8"cent-un");
    			BOOST_TEST(convert(199) == u8"cent-quatre-vingt-dix-neuf");
    			if(plurielSiPossible)
    				BOOST_TEST(convert(200) == u8"deux-cents");
    			else
    				BOOST_TEST(convert(200) == u8"deux-cent");
    			BOOST_TEST(convert(201) == u8"deux-cent-un");
    			BOOST_TEST(convert(299) == u8"deux-cent-quatre-vingt-dix-neuf");
    			if(plurielSiPossible)
    				BOOST_TEST(convert(900) == u8"neuf-cents");
    			else
    				BOOST_TEST(convert(900) == u8"neuf-cent");
    			BOOST_TEST(convert(901) == u8"neuf-cent-un");
    			if(plurielSiPossible)
    				BOOST_TEST(convert(980) == u8"neuf-cent-quatre-vingts");
    			else
    				BOOST_TEST(convert(980) == u8"neuf-cent-quatre-vingt");
    			BOOST_TEST(convert(999) == u8"neuf-cent-quatre-vingt-dix-neuf");
     
    			BOOST_TEST(convert(1000) == u8"mille");
    			BOOST_TEST(convert(1001) == u8"mille-un");
    			BOOST_TEST(convert(1999) == u8"mille-neuf-cent-quatre-vingt-dix-neuf");
    			BOOST_TEST(convert(2000) == u8"deux-mille");
    			BOOST_TEST(convert(2001) == u8"deux-mille-un");
    			BOOST_TEST(convert(2999) == u8"deux-mille-neuf-cent-quatre-vingt-dix-neuf");
     
    			BOOST_TEST(convert(80'000) == u8"quatre-vingt-mille");
    			if(plurielSiPossible)
    				BOOST_TEST(convert(80'080) == u8"quatre-vingt-mille-quatre-vingts");
    			else
    				BOOST_TEST(convert(80'080) == u8"quatre-vingt-mille-quatre-vingt");
     
    			BOOST_TEST(convert(200'000) == u8"deux-cent-mille");
    			BOOST_TEST(convert(200'001) == u8"deux-cent-mille-un");
    			if(plurielSiPossible)
    				BOOST_TEST(convert(900'900) == u8"neuf-cent-mille-neuf-cents");
    			else
    				BOOST_TEST(convert(900'900) == u8"neuf-cent-mille-neuf-cent");
     
    			BOOST_TEST(convert(1'000'000) == u8"un million");
    			BOOST_TEST(convert(1'000'001) == u8"un million un");
    			BOOST_TEST(convert(1'001'000) == u8"un million mille");
    			BOOST_TEST(convert(1'001'001) == u8"un million mille-un");
    			BOOST_TEST(convert(1'002'000) == u8"un million deux-mille");
    			BOOST_TEST(convert(2'000'000) == u8"deux millions");
    			BOOST_TEST(convert(2'000'001) == u8"deux millions un");
    			BOOST_TEST(convert(2'001'000) == u8"deux millions mille");
    			BOOST_TEST(convert(2'002'000) == u8"deux millions deux-mille");
     
    			if(plurielSiPossible)
    				BOOST_TEST(convert(80'080'080) == u8"quatre-vingts millions quatre-vingt-mille-quatre-vingts");
    			else
    				BOOST_TEST(convert(80'080'080) == u8"quatre-vingts millions quatre-vingt-mille-quatre-vingt");
     
    			BOOST_TEST(convert(200'000'000) == u8"deux-cents millions");
    			BOOST_TEST(convert(200'000'001) == u8"deux-cents millions un");
    			if(plurielSiPossible)
    				BOOST_TEST(convert(900'900'900) == u8"neuf-cents millions neuf-cent-mille-neuf-cents");
    			else
    				BOOST_TEST(convert(900'900'900) == u8"neuf-cents millions neuf-cent-mille-neuf-cent");
     
    			BOOST_TEST(convert(1'000'000'000) == u8"un milliard");
    			BOOST_TEST(convert(1'000'000'001) == u8"un milliard un");
    			BOOST_TEST(convert(1'000'001'000) == u8"un milliard mille");
    			BOOST_TEST(convert(1'000'001'001) == u8"un milliard mille-un");
    			BOOST_TEST(convert(1'000'002'000) == u8"un milliard deux-mille");
    			BOOST_TEST(convert(1'001'000'000) == u8"un milliard un million");
    			BOOST_TEST(convert(1'001'001'000) == u8"un milliard un million mille");
    			BOOST_TEST(convert(1'001'001'001) == u8"un milliard un million mille-un");
    			BOOST_TEST(convert(2'000'000'000) == u8"deux milliards");
    			BOOST_TEST(convert(2'000'000'001) == u8"deux milliards un");
     
    			if(plurielSiPossible)
    				BOOST_TEST(convert(-2'000'300'400) == u8"moins deux milliards trois-cent-mille-quatre-cents");
    			else
    				BOOST_TEST(convert(-2'000'300'400) == u8"moins deux milliards trois-cent-mille-quatre-cent");
    		}
    	}
    }
     
    BOOST_AUTO_TEST_SUITE_END()
    Quelques points importants à retenir :
    • Il faut éviter les fonctions de 2km. A la place, il faut décomposer les fonctions en sous-fonctions. Sinon, le code est plus difficile à analyser et contient plus facilement des bogues.
    • Il faut écrire des tests unitaires. Comme ça, quand on modifie le code, il devient facile et rapide de vérifier que ce qui marchait avant marche toujours.


    Edit 2017-11-05-22h20 : Séparation des fonctions convertNumberToFrenchUtf8String en paires de fonctions convertirNombreCardinalEnChaineFrancaiseUtf8 / convertirNombreOrdinalEnChaineFrancaiseUtf8.
    Edit 2017-11-05-22h42 : Petite amélioration du code.
    Edit 2017-11-05-22h48 : Correction d'un commentaire.
    Edit 2017-11-06-00h38 : Petite amélioration du code.
    Edit 2017-11-06-01h09 : Fusion des paires de fonctions convertirNombreCardinalEnChaineFrancaiseUtf8 / convertirNombreOrdinalEnChaineFrancaiseUtf8 en fonctions convertirNombreEnChaineFrancaiseUtf8 avec un paramètre booléen en plus.
    Edit 2017-11-06-20h00 : Factorisation des tests unitaires + modification d'un commentaire.
    Edit 2017-11-06-21h07 : Contrairement à ce que je croyais, même dans les rectifications orthographiques de 1990, il n'y a pas de tiret juste avant et juste après million(s) et milliard(s). Donc j'ai corrigé le code, les commentaires et les tests.
    Edit 2017-11-07-01h10 : Petite amélioration du code + correction d'une erreur de nommage.
    Edit 2017-11-07-19h26 : Renommage d'une fonction + retrait d'un #include inutile.

  10. #10
    Membre chevronné
    Avatar de DjmSoftware
    Homme Profil pro
    Responsable de compte
    Inscrit en
    Mars 2002
    Messages
    1 044
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Responsable de compte
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2002
    Messages : 1 044
    Points : 2 187
    Points
    2 187
    Billets dans le blog
    1
    Par défaut
    ce code est excellent et mérite sa place dans la FAQ c++
    Citation Envoyé par Pyramidev Voir le message
    Il faut aussi prendre en compte les cas où on écrit "quatre-vingts" au lieu de "quatre-vingt".

    Cette fois-ci, je vais publier une correction :

    FrenchUtility.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    #ifndef INCLUDE__PYRAMIDEV__FRENCH_UTILITY__H
    #define INCLUDE__PYRAMIDEV__FRENCH_UTILITY__H
     
    #include <string>
     
    namespace Pyramidev::FrenchUtility {
     
    	// Remarque : En français, on écrit "quatre-vingts pages" et "huit-cents ans",
    	// mais on écrit "la page quatre-vingt" et "l'an huit-cent".
    	// Plus de détails ici :
    	// http://parler-francais.eklablog.com/accord-de-vingt-cent-et-mille-a3877321
     
    	//! Exemples :
    	//! - Si nombre == 80'080'080 et plurielSiPossible == true, alors on retourne
    	//!   u8"quatre-vingts millions quatre-vingt-mille-quatre-vingts".
    	//! - Si nombre == 80'080'080 et plurielSiPossible == false, alors on retourne
    	//!   u8"quatre-vingts millions quatre-vingt-mille-quatre-vingt".
    	//! - Si nombre == 800'800'800 et plurielSiPossible == true, alors on retourne
    	//!   u8"huit-cents millions huit-cent-mille-huit-cents".
    	//! - Si nombre == 800'800'800 et plurielSiPossible == false, alors on retourne
    	//!   u8"huit-cents millions huit-cent-mille-huit-cent".
    	//! - Si nombre == -1'001'001, alors on retourne u8"moins un million mille-un".
    	std::string convertirNombreEnChaineFrancaiseUtf8(int      nombre, bool plurielSiPossible = true);
    	std::string convertirNombreEnChaineFrancaiseUtf8(unsigned nombre, bool plurielSiPossible = true);
     
    } // namespace Pyramidev::FrenchUtility
     
    #endif
    FrenchUtility.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    #include "Pyramidev/FrenchUtility.h"
     
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Weffc++"
    #pragma GCC diagnostic ignored "-Wold-style-cast"
    # include <boost/algorithm/string/join.hpp>
    #pragma GCC diagnostic pop
     
    #include <limits>
    #include <stdexcept>
    #include <string>
    #include <type_traits>
    #include <vector>
     
    namespace Pyramidev {
     
    namespace {
    	constexpr unsigned cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE = 3;
     
    	template<class TypeEntier>
    	constexpr TypeEntier partieEntiereLog1000(TypeEntier nombrePositif)
    	{
    		static_assert(std::numeric_limits<TypeEntier>::is_integer);
    		if(nombrePositif < 0)
    			throw std::logic_error(u8"nombre strictement négatif");
     
    		TypeEntier resultat = 0;
    		while(nombrePositif >= 1000) {
    			nombrePositif /= 1000;
    			++resultat;
    		}
    		return resultat;
    	}
     
    	template<class TypeNombre, class TypeEntier>
    	constexpr TypeNombre puissance(TypeNombre nombre, TypeEntier exposantPositif)
    	{
    		static_assert(std::is_integral_v<TypeNombre>);
    		static_assert(std::numeric_limits<TypeEntier>::is_integer);
    		if(exposantPositif < 0)
    			throw std::logic_error(u8"nombre strictement négatif");
    		if(nombre == 0 && exposantPositif == 0)
    			throw std::logic_error(u8"0 puissance 0 n'est pas défini.");
     
    		TypeNombre resultat = 1;
    		for(TypeEntier k = 0; k < exposantPositif; ++k)
    			resultat *= nombre;
    		return resultat;
    	}
     
    	std::string chaineNombreEntre1Et999Fois1000Puissance(unsigned nombre, unsigned exposant,
    	                                                     bool plurielSiPossible);
    	std::string chaineNombreEntre1Et999(unsigned nombre, bool plurielSiPossible);
    	std::string chaineNombreEntre1Et99(unsigned nombre, bool plurielSiPossible);
    	constexpr const char* motDe1000Puissance(unsigned exposant);
    	constexpr const char* motDe10FoisFacteurEntre1Et9(unsigned facteur);
    	constexpr const char* chaineNombreEntre1Et19(unsigned nombre);
    	constexpr const char* motDeChiffre(unsigned chiffre);
    }
     
    std::string FrenchUtility::convertirNombreEnChaineFrancaiseUtf8(int nombre, bool plurielSiPossible)
    {
    	const std::string prefixe = (nombre < 0) ? u8"moins " : u8"";
    	const auto nombrePositif = static_cast<unsigned>((nombre < 0) ? -nombre : nombre);
    	return prefixe + convertirNombreEnChaineFrancaiseUtf8(nombrePositif, plurielSiPossible);
    }
     
    std::string FrenchUtility::convertirNombreEnChaineFrancaiseUtf8(unsigned nombre, bool plurielSiPossible)
    {
    	if(nombre == 0)
    		return motDeChiffre(0);
     
    	std::vector<std::string> tripletsDeChiffres{};
     
    	const unsigned exposantMaximal = partieEntiereLog1000(nombre);
    		// Ex : exposantMaximal == 2 ssi nombre >= 1'000'000 && nombre < 1'000'000'000
     
    	{
    		constexpr unsigned exposantMaximalMaximal =
    			partieEntiereLog1000(std::numeric_limits<unsigned>::max());
    		static_assert(exposantMaximalMaximal <= cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE);
    			// Pour assurer la précondition de chaineNombreEntre1Et999Fois1000Puissance.
    	}
     
    	tripletsDeChiffres.reserve(exposantMaximal + 1);
     
    	for(int exposantSigne = exposantMaximal; exposantSigne >= 0; --exposantSigne)
    	{
    		const auto exposant = static_cast<unsigned>(exposantSigne);
     
    		const unsigned centainesDizainesEtUnites =
    			(nombre / puissance(1000, exposant)) % 1000;
     
    		// Exemple avec nombre == 12'003'450 :
    		// - Si exposant == 2, alors centainesDizainesEtUnites == 12
    		//   et on ajoute u8"douze millions" dans le vecteur tripletsDeChiffres.
    		// - Si exposant == 1, alors centainesDizainesEtUnites == 3
    		//   et on ajoute u8"trois-mille" dans le vecteur tripletsDeChiffres.
    		// - Si exposant == 0, alors centainesDizainesEtUnites == 450
    		//   et on ajoute u8"quatre-cent-cinquante" dans le vecteur tripletsDeChiffres.
     
    		if(centainesDizainesEtUnites > 0) {
    			tripletsDeChiffres.push_back(chaineNombreEntre1Et999Fois1000Puissance(
    				centainesDizainesEtUnites, exposant, plurielSiPossible
    			));
    		} else {
    			tripletsDeChiffres.push_back(u8"");
    		}
    	}
     
    	const size_t nbTripletsDeChiffres = tripletsDeChiffres.size();
    	assert(nbTripletsDeChiffres > 0);
     
    	// Récupérer la partie la plus grande.
    	std::string resultat = tripletsDeChiffres[0];
     
    	if(nombre > 1'000)
    	{
    		assert(nbTripletsDeChiffres >= 2);
    		const size_t indiceDesMilliers    = nbTripletsDeChiffres - 2;
    		const size_t indiceDernierePartie = nbTripletsDeChiffres - 1;
     
    		// Ajouter les éventuelles autres parties supérieures ou égales à mille.
    		// Exemple avec nombre == 12'003'450 :
    		// On ajoute u8" trois-mille" dans le résultat.
    		for(size_t k = 1; k <= indiceDesMilliers; ++k) {
    			if(tripletsDeChiffres[k].empty() == false) {
    				resultat += u8" ";
    				resultat += tripletsDeChiffres[k];
    			}
    		}
     
    		// Ajouter l'éventuelle autre partie inférieure à mille.
    		// Exemple avec nombre == 12'003'450 :
    		// On ajoute u8"-quatre-cent-cinquante" dans le résultat.
    		if(tripletsDeChiffres[indiceDernierePartie].empty() == false)
    		{
    			const bool ajouterTiret = (tripletsDeChiffres[indiceDesMilliers].empty() == false);
    			resultat += ajouterTiret ? u8"-" : u8" ";
    			resultat += tripletsDeChiffres[indiceDernierePartie];
    		}
    	}
     
    	assert(!resultat.empty());
    	return resultat;
    }
     
    namespace
    {
    	//! Exemple avec nombre == 300 :
    	//! - Si exposant == 0, alors on retourne "trois-cents" ou "trois-cent".
    	//! - Si exposant == 1, alors on retourne "trois-cent-mille".
    	//! - Si exposant == 2, alors on retourne "trois-cents millions".
    	//!
    	std::string chaineNombreEntre1Et999Fois1000Puissance(unsigned nombre, unsigned exposant,
    	                                                     bool plurielSiPossible)
    	{
    		assert(nombre >= 1);
    		assert(nombre <= 999);
    		assert(exposant <= cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE);
    			// car précondition de motDe1000Puissance
    		std::string resultat{};
    		switch(exposant) {
    			case 0:
    				resultat = chaineNombreEntre1Et999(nombre, plurielSiPossible);
    				break;
    			case 1:
    				if(nombre > 1) {
    					resultat = chaineNombreEntre1Et999(nombre, false);
    					resultat += u8"-";
    				}
    				resultat += motDe1000Puissance(1);
    				break;
    			default:
    				resultat = chaineNombreEntre1Et999(nombre, true);
    				resultat += u8" ";
    				resultat += motDe1000Puissance(exposant);
    				if(nombre > 1)
    					resultat += u8"s";
    		}
    		assert(!resultat.empty());
    		return resultat;
    	}
     
    	std::string chaineNombreEntre1Et999(unsigned nombre, bool plurielSiPossible)
    	{
    		assert(nombre >= 1);
    		assert(nombre <= 999);
    		const unsigned centaines        = nombre / 100;
    		const unsigned dizainesEtUnites = nombre % 100;
    		std::vector<std::string> resultat{};
    		if(centaines > 1)
    			resultat.push_back(motDeChiffre(centaines));
    		if(centaines > 0) {
    			if(plurielSiPossible && centaines > 1 && dizainesEtUnites == 0)
    				resultat.push_back(u8"cents");
    			else
    				resultat.push_back(u8"cent");
    		}
    		if(dizainesEtUnites > 0)
    			resultat.push_back(chaineNombreEntre1Et99(dizainesEtUnites, plurielSiPossible));
    		assert(!resultat.empty());
    		return boost::algorithm::join(resultat, u8"-");
    	}
     
    	std::string chaineNombreEntre1Et99(unsigned nombre, bool plurielSiPossible)
    	{
    		assert(nombre >= 1);
    		assert(nombre <= 99);
    		const unsigned dizaines = nombre / 10;
    		const unsigned unites   = nombre % 10;
    		std::string resultat{};
    		switch(dizaines) {
    			case 0:
    			case 1:
    				resultat = chaineNombreEntre1Et19(nombre);
    				break;
    			case 2:
    			case 3:
    			case 4:
    			case 5:
    				resultat = motDe10FoisFacteurEntre1Et9(dizaines);
    				if(unites == 1)
    					resultat += u8"-et";
    				if(unites >= 1) {
    					resultat += u8"-";
    					resultat += motDeChiffre(unites);
    				}
    				break;
    			case 6:
    			case 7:
    				resultat = motDe10FoisFacteurEntre1Et9(6);
    				if(nombre % 20 == 1)
    					resultat += u8"-et";
    				if(nombre % 20 >= 1) {
    					resultat += u8"-";
    					resultat += chaineNombreEntre1Et19(nombre % 20);
    				}
    				break;
    			case 8:
    			case 9:
    				resultat = motDe10FoisFacteurEntre1Et9(8);
    				if(plurielSiPossible && nombre % 20 == 0)
    					resultat += u8"s";
    				if(nombre % 20 >= 1) {
    					resultat += u8"-";
    					resultat += chaineNombreEntre1Et19(nombre % 20);
    				}
    				break;
    			default:
    				assert(false);
    		}
    		assert(!resultat.empty());
    		return resultat;
    	}
     
    	constexpr const char* motDe1000Puissance(unsigned exposant)
    	{
    		assert(exposant >= 1);
    		assert(exposant <= cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE);
    		static_assert(3 >= cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE);
    		switch(exposant) {
    			case 1: return u8"mille";
    			case 2: return u8"million";
    			case 3: return u8"milliard";
    			default:
    				throw std::logic_error(u8"unreachable code");
    		}
    	}
     
    	constexpr const char* motDe10FoisFacteurEntre1Et9(unsigned facteur)
    	{
    		assert(facteur >= 1);
    		assert(facteur <= 9);
    		switch(facteur) {
    			case 1: return chaineNombreEntre1Et19(10);
    			case 2: return u8"vingt";
    			case 3: return u8"trente";
    			case 4: return u8"quarante";
    			case 5: return u8"cinquante";
    			case 6: return u8"soixante";
    			case 7: return u8"soixante-dix";
    			case 8: return u8"quatre-vingt";
    			case 9: return u8"quatre-vingt-dix";
    			default:
    				throw std::logic_error(u8"unreachable code");
    		}
    	}
     
    	constexpr const char* chaineNombreEntre1Et19(unsigned nombre)
    	{
    		assert(nombre >= 1);
    		assert(nombre <= 19);
    		if(nombre >= 1 && nombre <= 9)
    			return motDeChiffre(nombre);
    		switch(nombre) {
    			case 10: return u8"dix";
    			case 11: return u8"onze";
    			case 12: return u8"douze";
    			case 13: return u8"treize";
    			case 14: return u8"quatorze";
    			case 15: return u8"quinze";
    			case 16: return u8"seize";
    			case 17: return u8"dix-sept";
    			case 18: return u8"dix-huit";
    			case 19: return u8"dix-neuf";
    			default:
    				throw std::logic_error(u8"unreachable code");
    		}
    	}
     
    	constexpr const char* motDeChiffre(unsigned chiffre)
    	{
    		assert(chiffre <= 9);
    		switch(chiffre) {
    			case 0: return u8"zéro";
    			case 1: return u8"un";
    			case 2: return u8"deux";
    			case 3: return u8"trois";
    			case 4: return u8"quatre";
    			case 5: return u8"cinq";
    			case 6: return u8"six";
    			case 7: return u8"sept";
    			case 8: return u8"huit";
    			case 9: return u8"neuf";
    			default:
    				throw std::logic_error(u8"unreachable code");
    		}
    	}
    }
     
    } // namespace Pyramidev
    FrenchUtilityTest.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Weffc++"
    #pragma GCC diagnostic ignored "-Wmissing-declarations"
    #pragma GCC diagnostic ignored "-Wold-style-cast"
    #pragma GCC diagnostic ignored "-Woverloaded-virtual"
    #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
    # include <boost/test/unit_test.hpp>
    #pragma GCC diagnostic pop
     
    #include "Pyramidev/FrenchUtility.h"
     
    BOOST_AUTO_TEST_SUITE(FrenchUtilityTest_conversionsDeNombresEnChaines)
     
    BOOST_AUTO_TEST_CASE(Test_convertirNombreEnChaineFrancaiseUtf8)
    {
    	for(bool plurielSiPossible : {true, false})
    	{
    		BOOST_TEST_CONTEXT("plurielSiPossible == " << (plurielSiPossible ? "true" : "false"))
    		{
     
    			auto convert = [plurielSiPossible](int number) {
    				return Pyramidev::FrenchUtility::convertirNombreEnChaineFrancaiseUtf8(
    					number, plurielSiPossible);
    			};
     
    			BOOST_TEST(convert(-1) == u8"moins un");
    			BOOST_TEST(convert(0)  == u8"zéro");
    			BOOST_TEST(convert(1)  == u8"un");
    			BOOST_TEST(convert(2)  == u8"deux");
    			BOOST_TEST(convert(3)  == u8"trois");
    			BOOST_TEST(convert(4)  == u8"quatre");
    			BOOST_TEST(convert(5)  == u8"cinq");
    			BOOST_TEST(convert(6)  == u8"six");
    			BOOST_TEST(convert(7)  == u8"sept");
    			BOOST_TEST(convert(8)  == u8"huit");
    			BOOST_TEST(convert(9)  == u8"neuf");
    			BOOST_TEST(convert(10) == u8"dix");
    			BOOST_TEST(convert(11) == u8"onze");
    			BOOST_TEST(convert(12) == u8"douze");
    			BOOST_TEST(convert(13) == u8"treize");
    			BOOST_TEST(convert(14) == u8"quatorze");
    			BOOST_TEST(convert(15) == u8"quinze");
    			BOOST_TEST(convert(16) == u8"seize");
    			BOOST_TEST(convert(17) == u8"dix-sept");
    			BOOST_TEST(convert(18) == u8"dix-huit");
    			BOOST_TEST(convert(19) == u8"dix-neuf");
    			BOOST_TEST(convert(20) == u8"vingt");
    			BOOST_TEST(convert(21) == u8"vingt-et-un");
    			BOOST_TEST(convert(22) == u8"vingt-deux");
    			BOOST_TEST(convert(29) == u8"vingt-neuf");
    			BOOST_TEST(convert(30) == u8"trente");
    			BOOST_TEST(convert(31) == u8"trente-et-un");
    			BOOST_TEST(convert(32) == u8"trente-deux");
    			BOOST_TEST(convert(39) == u8"trente-neuf");
    			BOOST_TEST(convert(40) == u8"quarante");
    			BOOST_TEST(convert(41) == u8"quarante-et-un");
    			BOOST_TEST(convert(42) == u8"quarante-deux");
    			BOOST_TEST(convert(49) == u8"quarante-neuf");
    			BOOST_TEST(convert(50) == u8"cinquante");
    			BOOST_TEST(convert(51) == u8"cinquante-et-un");
    			BOOST_TEST(convert(52) == u8"cinquante-deux");
    			BOOST_TEST(convert(59) == u8"cinquante-neuf");
    			BOOST_TEST(convert(60) == u8"soixante");
    			BOOST_TEST(convert(61) == u8"soixante-et-un");
    			BOOST_TEST(convert(62) == u8"soixante-deux");
    			BOOST_TEST(convert(69) == u8"soixante-neuf");
    			BOOST_TEST(convert(70) == u8"soixante-dix");
    			BOOST_TEST(convert(71) == u8"soixante-onze");
    			BOOST_TEST(convert(72) == u8"soixante-douze");
    			BOOST_TEST(convert(79) == u8"soixante-dix-neuf");
    			if(plurielSiPossible)
    				BOOST_TEST(convert(80) == u8"quatre-vingts");
    			else
    				BOOST_TEST(convert(80) == u8"quatre-vingt");
    			BOOST_TEST(convert(81) == u8"quatre-vingt-un");
    			BOOST_TEST(convert(82) == u8"quatre-vingt-deux");
    			BOOST_TEST(convert(89) == u8"quatre-vingt-neuf");
    			BOOST_TEST(convert(90) == u8"quatre-vingt-dix");
    			BOOST_TEST(convert(91) == u8"quatre-vingt-onze");
    			BOOST_TEST(convert(92) == u8"quatre-vingt-douze");
    			BOOST_TEST(convert(99) == u8"quatre-vingt-dix-neuf");
     
    			BOOST_TEST(convert(100) == u8"cent");
    			BOOST_TEST(convert(101) == u8"cent-un");
    			BOOST_TEST(convert(199) == u8"cent-quatre-vingt-dix-neuf");
    			if(plurielSiPossible)
    				BOOST_TEST(convert(200) == u8"deux-cents");
    			else
    				BOOST_TEST(convert(200) == u8"deux-cent");
    			BOOST_TEST(convert(201) == u8"deux-cent-un");
    			BOOST_TEST(convert(299) == u8"deux-cent-quatre-vingt-dix-neuf");
    			if(plurielSiPossible)
    				BOOST_TEST(convert(900) == u8"neuf-cents");
    			else
    				BOOST_TEST(convert(900) == u8"neuf-cent");
    			BOOST_TEST(convert(901) == u8"neuf-cent-un");
    			if(plurielSiPossible)
    				BOOST_TEST(convert(980) == u8"neuf-cent-quatre-vingts");
    			else
    				BOOST_TEST(convert(980) == u8"neuf-cent-quatre-vingt");
    			BOOST_TEST(convert(999) == u8"neuf-cent-quatre-vingt-dix-neuf");
     
    			BOOST_TEST(convert(1000) == u8"mille");
    			BOOST_TEST(convert(1001) == u8"mille-un");
    			BOOST_TEST(convert(1999) == u8"mille-neuf-cent-quatre-vingt-dix-neuf");
    			BOOST_TEST(convert(2000) == u8"deux-mille");
    			BOOST_TEST(convert(2001) == u8"deux-mille-un");
    			BOOST_TEST(convert(2999) == u8"deux-mille-neuf-cent-quatre-vingt-dix-neuf");
     
    			BOOST_TEST(convert(80'000) == u8"quatre-vingt-mille");
    			if(plurielSiPossible)
    				BOOST_TEST(convert(80'080) == u8"quatre-vingt-mille-quatre-vingts");
    			else
    				BOOST_TEST(convert(80'080) == u8"quatre-vingt-mille-quatre-vingt");
     
    			BOOST_TEST(convert(200'000) == u8"deux-cent-mille");
    			BOOST_TEST(convert(200'001) == u8"deux-cent-mille-un");
    			if(plurielSiPossible)
    				BOOST_TEST(convert(900'900) == u8"neuf-cent-mille-neuf-cents");
    			else
    				BOOST_TEST(convert(900'900) == u8"neuf-cent-mille-neuf-cent");
     
    			BOOST_TEST(convert(1'000'000) == u8"un million");
    			BOOST_TEST(convert(1'000'001) == u8"un million un");
    			BOOST_TEST(convert(1'001'000) == u8"un million mille");
    			BOOST_TEST(convert(1'001'001) == u8"un million mille-un");
    			BOOST_TEST(convert(1'002'000) == u8"un million deux-mille");
    			BOOST_TEST(convert(2'000'000) == u8"deux millions");
    			BOOST_TEST(convert(2'000'001) == u8"deux millions un");
    			BOOST_TEST(convert(2'001'000) == u8"deux millions mille");
    			BOOST_TEST(convert(2'002'000) == u8"deux millions deux-mille");
     
    			if(plurielSiPossible)
    				BOOST_TEST(convert(80'080'080) == u8"quatre-vingts millions quatre-vingt-mille-quatre-vingts");
    			else
    				BOOST_TEST(convert(80'080'080) == u8"quatre-vingts millions quatre-vingt-mille-quatre-vingt");
     
    			BOOST_TEST(convert(200'000'000) == u8"deux-cents millions");
    			BOOST_TEST(convert(200'000'001) == u8"deux-cents millions un");
    			if(plurielSiPossible)
    				BOOST_TEST(convert(900'900'900) == u8"neuf-cents millions neuf-cent-mille-neuf-cents");
    			else
    				BOOST_TEST(convert(900'900'900) == u8"neuf-cents millions neuf-cent-mille-neuf-cent");
     
    			BOOST_TEST(convert(1'000'000'000) == u8"un milliard");
    			BOOST_TEST(convert(1'000'000'001) == u8"un milliard un");
    			BOOST_TEST(convert(1'000'001'000) == u8"un milliard mille");
    			BOOST_TEST(convert(1'000'001'001) == u8"un milliard mille-un");
    			BOOST_TEST(convert(1'000'002'000) == u8"un milliard deux-mille");
    			BOOST_TEST(convert(1'001'000'000) == u8"un milliard un million");
    			BOOST_TEST(convert(1'001'001'000) == u8"un milliard un million mille");
    			BOOST_TEST(convert(1'001'001'001) == u8"un milliard un million mille-un");
    			BOOST_TEST(convert(2'000'000'000) == u8"deux milliards");
    			BOOST_TEST(convert(2'000'000'001) == u8"deux milliards un");
     
    			if(plurielSiPossible)
    				BOOST_TEST(convert(-2'000'300'400) == u8"moins deux milliards trois-cent-mille-quatre-cents");
    			else
    				BOOST_TEST(convert(-2'000'300'400) == u8"moins deux milliards trois-cent-mille-quatre-cent");
    		}
    	}
    }
     
    BOOST_AUTO_TEST_SUITE_END()
    Quelques points importants à retenir :
    • Il faut éviter les fonctions de 2km. A la place, il faut décomposer les fonctions en sous-fonctions. Sinon, le code est plus difficile à analyser et contient plus facilement des bogues.
    • Il faut écrire des tests unitaires. Comme ça, quand on modifie le code, il devient facile et rapide de vérifier que ce qui marchait avant marche toujours.


    Edit 2017-11-05-22h20 : Séparation des fonctions convertNumberToFrenchUtf8String en paires de fonctions convertirNombreCardinalEnChaineFrancaiseUtf8 / convertirNombreOrdinalEnChaineFrancaiseUtf8.
    Edit 2017-11-05-22h42 : Petite amélioration du code.
    Edit 2017-11-05-22h48 : Correction d'un commentaire.
    Edit 2017-11-06-00h38 : Petite amélioration du code.
    Edit 2017-11-06-01h09 : Fusion des paires de fonctions convertirNombreCardinalEnChaineFrancaiseUtf8 / convertirNombreOrdinalEnChaineFrancaiseUtf8 en fonctions convertirNombreEnChaineFrancaiseUtf8 avec un paramètre booléen en plus.
    Edit 2017-11-06-20h00 : Factorisation des tests unitaires + modification d'un commentaire.
    Edit 2017-11-06-21h07 : Contrairement à ce que je croyais, même dans les rectifications orthographiques de 1990, il n'y a pas de tiret juste avant et juste après million(s) et milliard(s). Donc j'ai corrigé le code, les commentaires et les tests.
    Edit 2017-11-07-01h10 : Petite amélioration du code + correction d'une erreur de nommage.
    Edit 2017-11-07-19h26 : Renommage d'une fonction + retrait d'un #include inutile.
    vous trouverez mes tutoriels à l'adresse suivante: http://djmsoftware.developpez.com/
    je vous en souhaite une excellente lecture ...

    A lire : Les règles du forum

  11. #11
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 471
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 471
    Points : 6 110
    Points
    6 110
    Par défaut
    Citation Envoyé par DjmSoftware Voir le message
    ce code est excellent et mérite sa place dans la FAQ c++
    Merci. Mais dans quelle section de la FAQ ? Et pour illustrer quel point ?
    • La décomposition d'une fonction complexe en sous-fontions ?
    • Les tests unitaires ?


    S'il s'agit d'avoir un outil qui convertit un nombre en lettres en français, alors il manque encore quelques fonctionnalités :
    • le support de billion, billiard et trillion pour les entiers de 64 bits,
    • une option pour écrire moins de tirets, comme avant la réforme orthographique de 1990 et
    • une option pour écrire septante, octante et nonante, par exemple pour les français québécois.

    Alors, étant donné le cumul des options, à la place de fonctions à 4 paramètres dont 3 avec des arguments par défauts, on pourrait utiliser le named parameter idiom.

  12. #12
    Membre chevronné
    Avatar de DjmSoftware
    Homme Profil pro
    Responsable de compte
    Inscrit en
    Mars 2002
    Messages
    1 044
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Suisse

    Informations professionnelles :
    Activité : Responsable de compte
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2002
    Messages : 1 044
    Points : 2 187
    Points
    2 187
    Billets dans le blog
    1
    Par défaut
    bonjour
    pour ma part dans 2 endroits:
    FAQ C++ Builder
    • La décomposition d'une fonction complexe en sous-fontions
    • Test unitaires



    cdlt
    vous trouverez mes tutoriels à l'adresse suivante: http://djmsoftware.developpez.com/
    je vous en souhaite une excellente lecture ...

    A lire : Les règles du forum

  13. #13
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Attention toutefois:
    Il n'y a pas de trait d'union avant "cent", "cents" et "mille".
    C'est "huit cents" et "mille neuf cent quatre-vingt-sept"
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  14. #14
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 471
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 471
    Points : 6 110
    Points
    6 110
    Par défaut
    Citation Envoyé par DjmSoftware Voir le message
    FAQ C++ Builder
    Mon code compile avec GCC, mais ça m'étonnerait qu'il compile avec BCC32 (le compilateur 32 bits de C++ Builder). En effet, ce dernier est très en retard sur le C++11/14/17.

    Citation Envoyé par ternel Voir le message
    Attention toutefois:
    Il n'y a pas de trait d'union avant "cent", "cents" et "mille".
    C'est "huit cents" et "mille neuf cent quatre-vingt-sept"
    Dans les rectifications orthographiques de 1990, il y a des tirets partout, jusqu'à 999 999.
    C'est pour ça que, dans mon précédent message, j'ai dit qu'il manquait encore quelques fonctionnalités, dont "une option pour écrire moins de tirets, comme avant la réforme orthographique de 1990".

    Edit 2017-11-22-01h25 :

    A un moment, j'avais lu qu'il ne fallait pas écrire de tiret avant et après les mots million, milliard, etc., même avec les rectifications orthographiques de 1990. Mais je suis tombé sur des sources qui vont dans l'autre sens.

    Extrait de la page http://www.alsace.iufm.fr/espace.per...e_ecriture.htm :
    « "Il est vrai que, dans un premier temps, le grand grammairien belge André Goosse avait fait une autre interprétation, excluant "million" et "milliard" de la règle. Mais le texte officiel ne fait pas cette distinction, qui ne fait qu'induire une complication supplémentaire ; dans l'exposé des nouvelles règles que donne le Réseau pour la nouvelle orthographe sur son site, on trouve notamment l'exemple "un-million-cent" (http://www.renouvo.org/regles.php)."
    Ce qui se trouve confirmé par cet extrait d'un article du site de l'Académie Française : "Cependant, il est également possible, en accord avec les Rectifications de l’orthographe proposées par le Conseil supérieur de la langue française et parues au Journal officiel du 6 décembre 1990 (partie II), de lier par un trait d’union tous les éléments qui composent le nombre, sans exception." »

    La citation de l'Académie française est disponible ici : http://www.academie-francaise.fr/que...cord-em-strong

    Du coup, je vais de nouveau corriger le code.

  15. #15
    Expert éminent
    Avatar de Pyramidev
    Homme Profil pro
    Développeur
    Inscrit en
    Avril 2016
    Messages
    1 471
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur

    Informations forums :
    Inscription : Avril 2016
    Messages : 1 471
    Points : 6 110
    Points
    6 110
    Par défaut
    Voici la nouvelle version du code.

    ScripteurNombreFrancais.h :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    #ifndef INCLUDE__DENAV__SCRIPTEUR_NOMBRE_FRANCAIS__H
    #define INCLUDE__DENAV__SCRIPTEUR_NOMBRE_FRANCAIS__H
     
    #include <string>
     
    namespace denav {
     
    //! \brief  Scripteur de nombres en lettres en français.
    //! \author Denis Navarro
    //!
    //! Par exemple, denav::ScripteurNombreFrancais{}.versUtf8(42) retourne la chaîne u8"quarante-deux".
    //! \n\n
    //! Références :
    //! - http://www.academie-francaise.fr/questions-de-langue#57_strong-em-nombres-criture-lecture-accord-em-strong
    //! - http://www.alsace.iufm.fr/espace.personnel.dominique.pernoux/web/ressource_1/page_ecriture.htm
    //!
    class ScripteurNombreFrancais final {
    public:
    	constexpr ScripteurNombreFrancais() noexcept :
    		m_adjNumOrdinal{false},
    		m_tiretsPartout{false},
    		m_commeAuQuebec{false}
    	{}
    	//! Explication : En français, on écrit "huit cents ans" mais "l'an huit cent".
    	//! Dans le deuxième cas, il s'agit d'un adjectif numéral ordinal.
    	constexpr ScripteurNombreFrancais& modeAdjectifNumeralOrdinal() noexcept {
    		m_adjNumOrdinal = true;
    		return *this;
    	}
    	constexpr ScripteurNombreFrancais& appliquerRectifications1990() noexcept {
    		m_tiretsPartout = true;
    		return *this;
    	}
    	constexpr ScripteurNombreFrancais& autoriserSeptanteOctanteEtNonante() noexcept {
    		m_commeAuQuebec = true;
    		return *this;
    	}
    	std::string versUtf8(int                nombre) const;
    	std::string versUtf8(unsigned           nombre) const;
    	std::string versUtf8(long               nombre) const;
    	std::string versUtf8(unsigned long      nombre) const;
    	std::string versUtf8(long long          nombre) const;
    	std::string versUtf8(unsigned long long nombre) const;
    private:
    	bool m_adjNumOrdinal; //!< Ex : u8"huit cent" au lieu de u8"huit cents".
    	bool m_tiretsPartout; //!< Ex : u8"moins-cent-vingt-trois" au lieu de u8"moins cent vingt-trois".
    	bool m_commeAuQuebec; //!< Ex : u8"septante" au lieu de u8"soixante-dix".
    };
     
    } // namespace denav
     
    #endif
    ScripteurNombreFrancais.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    #include "denav/ScripteurNombreFrancais.h"
     
    #include <algorithm>
    #include <cassert>
    #include <limits>
    #include <stdexcept>
    #include <string>
     
    namespace denav {
     
    namespace {
     
    	constexpr unsigned cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE = 6;
     
    	constexpr unsigned partieEntiereLog1000(uintmax_t nombre)
    	{
    		unsigned resultat = 0;
    		while(nombre >= 1000) {
    			nombre /= 1000;
    			++resultat;
    		}
    		return resultat;
    	}
     
    	constexpr uintmax_t puissance(uintmax_t nombre, unsigned exposant)
    	{
    		assert(nombre != 0 || exposant != 0);
    		uintmax_t resultat = 1;
    		for(unsigned k = 0; k < exposant; ++k)
    			resultat *= nombre;
    		return resultat;
    	}
     
    	template<class TypeEntier>
    	std::string versUtf8Impl(
    		TypeEntier nombre, bool adjNumOrdinal, bool tiretsPartout, bool commeAuQuebec);
     
    	std::string convertirNombrePositifEnChaineFrancaiseUtf8(
    		uintmax_t nombre, bool adjNumOrdinal, bool commeAuQuebec);
     
    	std::string chaineNombreEntre1Et999Fois1000Puissance(
    		unsigned nombre, unsigned exposant, bool adjNumOrdinal, bool commeAuQuebec);
     
    	std::string chaineNombreEntre1Et999(unsigned nombre, bool adjNumOrdinal, bool commeAuQuebec);
    	std::string chaineNombreEntre1Et99 (unsigned nombre, bool adjNumOrdinal, bool commeAuQuebec);
    	constexpr char const* motDe1000Puissance(unsigned exposant);
    	constexpr char const* motQuebequoisDe10FoisFacteurEntre1Et9(unsigned facteur);
    	constexpr char const* chaineNombreEntre1Et19(unsigned nombre);
    	constexpr char const* motDeChiffre(unsigned chiffre);
    }
     
    std::string ScripteurNombreFrancais::versUtf8(int nombre) const {
    	return versUtf8Impl(nombre, m_adjNumOrdinal, m_tiretsPartout, m_commeAuQuebec);
    }
    std::string ScripteurNombreFrancais::versUtf8(unsigned nombre) const {
    	return versUtf8Impl(nombre, m_adjNumOrdinal, m_tiretsPartout, m_commeAuQuebec);
    }
    std::string ScripteurNombreFrancais::versUtf8(long nombre) const {
    	return versUtf8Impl(nombre, m_adjNumOrdinal, m_tiretsPartout, m_commeAuQuebec);
    }
    std::string ScripteurNombreFrancais::versUtf8(unsigned long nombre) const {
    	return versUtf8Impl(nombre, m_adjNumOrdinal, m_tiretsPartout, m_commeAuQuebec);
    }
    std::string ScripteurNombreFrancais::versUtf8(long long nombre) const {
    	return versUtf8Impl(nombre, m_adjNumOrdinal, m_tiretsPartout, m_commeAuQuebec);
    }
    std::string ScripteurNombreFrancais::versUtf8(unsigned long long nombre) const {
    	return versUtf8Impl(nombre, m_adjNumOrdinal, m_tiretsPartout, m_commeAuQuebec);
    }
     
    namespace {
     
    	template<class TypeEntier>
    	std::string versUtf8Impl(
    		TypeEntier nombre, bool adjNumOrdinal, bool tiretsPartout, bool commeAuQuebec)
    	{
    		static_assert(std::numeric_limits<TypeEntier>::is_integer);
    		std::string resultat{};
    		if(nombre >= 0) {
    			resultat = convertirNombrePositifEnChaineFrancaiseUtf8(nombre, adjNumOrdinal, commeAuQuebec);
    		} else {
    			auto const nombrePlusUn = nombre + 1; // pour pouvoir calculer l'opposé sans overflow
    			auto const nombreOppose = static_cast<uintmax_t>(-nombrePlusUn) + UINTMAX_C(1);
    			resultat = u8"moins " +
    				convertirNombrePositifEnChaineFrancaiseUtf8(nombreOppose, adjNumOrdinal, commeAuQuebec);
    		}
    		if(tiretsPartout)
    			std::replace(resultat.begin(), resultat.end(), u8' ', u8'-');
    		assert(!resultat.empty());
    		return resultat;
    	}
     
    	std::string convertirNombrePositifEnChaineFrancaiseUtf8(
    		uintmax_t nombre, bool adjNumOrdinal, bool commeAuQuebec)
    	{
    		if(nombre == 0)
    			return motDeChiffre(0);
     
    		unsigned const exposantMaximal = partieEntiereLog1000(nombre);
    			// Ex : exposantMaximal == 2 ssi nombre >= 1'000'000 && nombre < 1'000'000'000.
     
    		{
    			constexpr unsigned exposantMaximalMaximal =
    				partieEntiereLog1000(std::numeric_limits<uintmax_t>::max());
    			static_assert(exposantMaximalMaximal <= cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE);
    				// pour assurer la précondition de chaineNombreEntre1Et999Fois1000Puissance
    		}
     
    		std::string resultat{};
     
    		for(unsigned offset = 0; offset <= exposantMaximal; ++offset)
    		{
    			unsigned const exposant = exposantMaximal - offset;
     
    			auto const centainesDizainesEtUnites = static_cast<unsigned>(
    				(nombre / puissance(1000, exposant)) % 1000
    			);
     
    			// Exemple avec nombre == 12'003'450 :
    			// - Si exposant == 2, alors centainesDizainesEtUnites == 12
    			//   et on ajoute u8"douze millions" dans le résultat.
    			// - Si exposant == 1, alors centainesDizainesEtUnites == 3
    			//   et on ajoute u8" trois mille" dans le résultat.
    			// - Si exposant == 0, alors centainesDizainesEtUnites == 450
    			//   et on ajoute u8" quatre cent cinquante" dans le résultat.
     
    			if(centainesDizainesEtUnites >= 1)
    			{
    				if(!resultat.empty())
    					resultat += u8' ';
    				resultat += chaineNombreEntre1Et999Fois1000Puissance(
    					centainesDizainesEtUnites, exposant, adjNumOrdinal, commeAuQuebec
    				);
    			}
    		}
     
    		assert(!resultat.empty());
    		return resultat;
    	}
     
    	//! Exemple avec nombre == 300 :
    	//! - Si exposant == 0, alors on retourne u8"trois cents" ou u8"trois cent".
    	//! - Si exposant == 1, alors on retourne u8"trois cent mille".
    	//! - Si exposant == 2, alors on retourne u8"trois cents millions".
    	//!
    	std::string chaineNombreEntre1Et999Fois1000Puissance(
    		unsigned nombre, unsigned exposant, bool adjNumOrdinal, bool commeAuQuebec)
    	{
    		assert(nombre >= 1);
    		assert(nombre <= 999);
    		assert(exposant <= cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE);
    			// car précondition de motDe1000Puissance
    		std::string resultat{};
    		switch(exposant) {
    			case 0:
    				resultat = chaineNombreEntre1Et999(nombre, adjNumOrdinal, commeAuQuebec);
    				break;
    			case 1:
    				if(nombre >= 2) {
    					adjNumOrdinal = true;
    					resultat = chaineNombreEntre1Et999(nombre, adjNumOrdinal, commeAuQuebec);
    					resultat += u8' ';
    				}
    				resultat += motDe1000Puissance(1);
    				break;
    			default:
    				adjNumOrdinal = false;
    				resultat = chaineNombreEntre1Et999(nombre, adjNumOrdinal, commeAuQuebec);
    				resultat += u8' ';
    				resultat += motDe1000Puissance(exposant);
    				if(nombre >= 2)
    					resultat += u8's';
    		}
    		assert(!resultat.empty());
    		return resultat;
    	}
     
    	std::string chaineNombreEntre1Et999(unsigned nombre, bool adjNumOrdinal, bool commeAuQuebec)
    	{
    		assert(nombre >= 1);
    		assert(nombre <= 999);
    		unsigned const centaines        = nombre / 100;
    		unsigned const dizainesEtUnites = nombre % 100;
    		std::string resultat{};
    		if(centaines >= 1) {
    			if(centaines == 1) {
    				resultat = u8"cent";
    			} else {
    				resultat = motDeChiffre(centaines);
    				resultat += u8" cent";
    				if(!adjNumOrdinal && dizainesEtUnites == 0)
    					resultat += u8's';
    			}
    		}
    		if(dizainesEtUnites >= 1) {
    			if(!resultat.empty())
    				resultat += u8' ';
    			resultat += chaineNombreEntre1Et99(dizainesEtUnites, adjNumOrdinal, commeAuQuebec);
    		}
    		assert(!resultat.empty());
    		return resultat;
    	}
     
    	std::string chaineNombreEntre1Et99(unsigned nombre, bool adjNumOrdinal, bool commeAuQuebec)
    	{
    		assert(nombre >= 1);
    		assert(nombre <= 99);
    		std::string resultat{};
    		if(nombre <= 19) {
    			resultat = chaineNombreEntre1Et19(nombre);
    		} else if(nombre <= 69 || commeAuQuebec) {
    			unsigned const dizaines = nombre / 10;
    			unsigned const unites   = nombre % 10;
    			resultat = motQuebequoisDe10FoisFacteurEntre1Et9(dizaines);
    			if(unites >= 1) {
    				if(unites == 1) {
    					resultat += u8" et un";
    				} else {
    					resultat += u8'-';
    					resultat += motDeChiffre(unites);
    				}
    			}
    		} else if(nombre <= 79) {
    			resultat = motQuebequoisDe10FoisFacteurEntre1Et9(6);
    			resultat += (nombre == 71) ? u8" et " : u8"-";
    			resultat += chaineNombreEntre1Et19(nombre - 60);
    		} else {
    			resultat = u8"quatre-vingt";
    			if(nombre == 80) {
    				if(!adjNumOrdinal)
    					resultat += u8's';
    			} else {
    				resultat += u8'-';
    				resultat += chaineNombreEntre1Et19(nombre - 80);
    			}
    		}
    		assert(!resultat.empty());
    		return resultat;
    	}
     
    	constexpr char const* motDe1000Puissance(unsigned exposant)
    	{
    		assert(exposant >= 1);
    		assert(exposant <= cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE);
    		static_assert(6 >= cEXPOSANT_MAXIMAL_DE_PUISSANCE_DE_MILLE);
    		switch(exposant) {
    			case 1: return u8"mille";
    			case 2: return u8"million";
    			case 3: return u8"milliard";
    			case 4: return u8"billion";
    			case 5: return u8"billiard";
    			case 6: return u8"trillion";
    			default:
    				throw std::logic_error(u8"unreachable code");
    		}
    	}
     
    	constexpr char const* motQuebequoisDe10FoisFacteurEntre1Et9(unsigned facteur)
    	{
    		assert(facteur >= 1);
    		assert(facteur <= 9);
    		switch(facteur) {
    			case 1: return chaineNombreEntre1Et19(10);
    			case 2: return u8"vingt";
    			case 3: return u8"trente";
    			case 4: return u8"quarante";
    			case 5: return u8"cinquante";
    			case 6: return u8"soixante";
    			case 7: return u8"septante";
    			case 8: return u8"octante";
    			case 9: return u8"nonante";
    			default:
    				throw std::logic_error(u8"unreachable code");
    		}
    	}
     
    	constexpr char const* chaineNombreEntre1Et19(unsigned nombre)
    	{
    		assert(nombre >= 1);
    		assert(nombre <= 19);
    		if(nombre >= 1 && nombre <= 9)
    			return motDeChiffre(nombre);
    		switch(nombre) {
    			case 10: return u8"dix";
    			case 11: return u8"onze";
    			case 12: return u8"douze";
    			case 13: return u8"treize";
    			case 14: return u8"quatorze";
    			case 15: return u8"quinze";
    			case 16: return u8"seize";
    			case 17: return u8"dix-sept";
    			case 18: return u8"dix-huit";
    			case 19: return u8"dix-neuf";
    			default:
    				throw std::logic_error(u8"unreachable code");
    		}
    	}
     
    	constexpr char const* motDeChiffre(unsigned chiffre)
    	{
    		assert(chiffre <= 9);
    		switch(chiffre) {
    			case 0: return u8"zéro";
    			case 1: return u8"un";
    			case 2: return u8"deux";
    			case 3: return u8"trois";
    			case 4: return u8"quatre";
    			case 5: return u8"cinq";
    			case 6: return u8"six";
    			case 7: return u8"sept";
    			case 8: return u8"huit";
    			case 9: return u8"neuf";
    			default:
    				throw std::logic_error(u8"unreachable code");
    		}
    	}
    }
     
    } // namespace denav
    ScripteurNombreFrancaisTest.cpp :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    #pragma GCC diagnostic push
    #pragma GCC diagnostic ignored "-Weffc++"
    #pragma GCC diagnostic ignored "-Wold-style-cast"
    #pragma GCC diagnostic ignored "-Woverloaded-virtual"
    #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
    # include <boost/test/unit_test.hpp>
    #pragma GCC diagnostic pop
     
    #include "denav/ScripteurNombreFrancais.h"
     
    #include <cstdint>
     
    BOOST_AUTO_TEST_SUITE(ScripteurNombreFrancaisTest_testSuite)
     
    BOOST_AUTO_TEST_CASE(Test_signeVariable_tiretsVariables)
    {
    	for(bool adjNumOrdinal : {true, false}) {
    	for(bool tiretsPartout : {true, false}) {
    	for(bool commeAuQuebec : {true, false}) {
    	BOOST_TEST_CONTEXT("adjNumOrdinal == " << (adjNumOrdinal ? "true" : "false")) {
    	BOOST_TEST_CONTEXT("tiretsPartout == " << (tiretsPartout ? "true" : "false")) {
    	BOOST_TEST_CONTEXT("commeAuQuebec == " << (commeAuQuebec ? "true" : "false")) {
     
    		denav::ScripteurNombreFrancais scripteur{};
    		if(adjNumOrdinal)
    			scripteur.modeAdjectifNumeralOrdinal();
    		if(tiretsPartout)
    			scripteur.appliquerRectifications1990();
    		if(commeAuQuebec)
    			scripteur.autoriserSeptanteOctanteEtNonante();
     
    		auto const convert = [&scripteur](auto nombre) {
    			return scripteur.versUtf8(nombre);
    		};
     
    		BOOST_TEST(convert(0)  == u8"zéro");
    		BOOST_TEST(convert(0U) == u8"zéro");
    		BOOST_TEST(convert(1)  == u8"un");
    		BOOST_TEST(convert(1U) == u8"un");
    		if(tiretsPartout) {
    			BOOST_TEST(convert(-1)     == u8"moins-un");
    			BOOST_TEST(convert(54321)  == u8"cinquante-quatre-mille-trois-cent-vingt-et-un");
    			BOOST_TEST(convert(54321U) == u8"cinquante-quatre-mille-trois-cent-vingt-et-un");
    			BOOST_TEST(convert(-54321) == u8"moins-cinquante-quatre-mille-trois-cent-vingt-et-un");
    		} else {
    			BOOST_TEST(convert(-1)     == u8"moins un");
    			BOOST_TEST(convert(54321)  == u8"cinquante-quatre mille trois cent vingt et un");
    			BOOST_TEST(convert(54321)  == u8"cinquante-quatre mille trois cent vingt et un");
    			BOOST_TEST(convert(-54321) == u8"moins cinquante-quatre mille trois cent vingt et un");
    		}
    	}
    	}
    	}
    	}
    	}
    	}
    }
     
    BOOST_AUTO_TEST_CASE(Test_nombresPositifs_tiretsNormaux)
    {
    	for(bool adjNumOrdinal : {true, false}) {
    	for(bool commeAuQuebec : {true, false}) {
    	BOOST_TEST_CONTEXT("adjNumOrdinal == " << (adjNumOrdinal ? "true" : "false")) {
    	BOOST_TEST_CONTEXT("commeAuQuebec == " << (commeAuQuebec ? "true" : "false")) {
     
    		denav::ScripteurNombreFrancais scripteur{};
    		if(adjNumOrdinal)
    			scripteur.modeAdjectifNumeralOrdinal();
    		if(commeAuQuebec)
    			scripteur.autoriserSeptanteOctanteEtNonante();
     
    		auto const convert = [&scripteur](auto nombre) {
    			return scripteur.versUtf8(nombre);
    		};
     
    		BOOST_TEST(convert(0)  == u8"zéro");
    		BOOST_TEST(convert(1)  == u8"un");
    		BOOST_TEST(convert(2)  == u8"deux");
    		BOOST_TEST(convert(3)  == u8"trois");
    		BOOST_TEST(convert(4)  == u8"quatre");
    		BOOST_TEST(convert(5)  == u8"cinq");
    		BOOST_TEST(convert(6)  == u8"six");
    		BOOST_TEST(convert(7)  == u8"sept");
    		BOOST_TEST(convert(8)  == u8"huit");
    		BOOST_TEST(convert(9)  == u8"neuf");
    		BOOST_TEST(convert(10) == u8"dix");
    		BOOST_TEST(convert(11) == u8"onze");
    		BOOST_TEST(convert(12) == u8"douze");
    		BOOST_TEST(convert(13) == u8"treize");
    		BOOST_TEST(convert(14) == u8"quatorze");
    		BOOST_TEST(convert(15) == u8"quinze");
    		BOOST_TEST(convert(16) == u8"seize");
    		BOOST_TEST(convert(17) == u8"dix-sept");
    		BOOST_TEST(convert(18) == u8"dix-huit");
    		BOOST_TEST(convert(19) == u8"dix-neuf");
    		BOOST_TEST(convert(20) == u8"vingt");
    		BOOST_TEST(convert(21) == u8"vingt et un");
    		BOOST_TEST(convert(22) == u8"vingt-deux");
    		BOOST_TEST(convert(29) == u8"vingt-neuf");
    		BOOST_TEST(convert(30) == u8"trente");
    		BOOST_TEST(convert(31) == u8"trente et un");
    		BOOST_TEST(convert(32) == u8"trente-deux");
    		BOOST_TEST(convert(39) == u8"trente-neuf");
    		BOOST_TEST(convert(40) == u8"quarante");
    		BOOST_TEST(convert(41) == u8"quarante et un");
    		BOOST_TEST(convert(42) == u8"quarante-deux");
    		BOOST_TEST(convert(49) == u8"quarante-neuf");
    		BOOST_TEST(convert(50) == u8"cinquante");
    		BOOST_TEST(convert(51) == u8"cinquante et un");
    		BOOST_TEST(convert(52) == u8"cinquante-deux");
    		BOOST_TEST(convert(59) == u8"cinquante-neuf");
    		BOOST_TEST(convert(60) == u8"soixante");
    		BOOST_TEST(convert(61) == u8"soixante et un");
    		BOOST_TEST(convert(62) == u8"soixante-deux");
    		BOOST_TEST(convert(69) == u8"soixante-neuf");
    		if(commeAuQuebec) {
    			BOOST_TEST(convert(70) == u8"septante");
    			BOOST_TEST(convert(71) == u8"septante et un");
    			BOOST_TEST(convert(72) == u8"septante-deux");
    			BOOST_TEST(convert(79) == u8"septante-neuf");
    			BOOST_TEST(convert(80) == u8"octante");
    			BOOST_TEST(convert(81) == u8"octante et un");
    			BOOST_TEST(convert(82) == u8"octante-deux");
    			BOOST_TEST(convert(89) == u8"octante-neuf");
    			BOOST_TEST(convert(90) == u8"nonante");
    			BOOST_TEST(convert(91) == u8"nonante et un");
    			BOOST_TEST(convert(92) == u8"nonante-deux");
    			BOOST_TEST(convert(99) == u8"nonante-neuf");
    		} else {
    			BOOST_TEST(convert(70) == u8"soixante-dix");
    			BOOST_TEST(convert(71) == u8"soixante et onze");
    			BOOST_TEST(convert(72) == u8"soixante-douze");
    			BOOST_TEST(convert(79) == u8"soixante-dix-neuf");
    			BOOST_TEST(convert(80) == (adjNumOrdinal ? u8"quatre-vingt" : u8"quatre-vingts"));
    			BOOST_TEST(convert(81) == u8"quatre-vingt-un");
    			BOOST_TEST(convert(82) == u8"quatre-vingt-deux");
    			BOOST_TEST(convert(89) == u8"quatre-vingt-neuf");
    			BOOST_TEST(convert(90) == u8"quatre-vingt-dix");
    			BOOST_TEST(convert(91) == u8"quatre-vingt-onze");
    			BOOST_TEST(convert(92) == u8"quatre-vingt-douze");
    			BOOST_TEST(convert(99) == u8"quatre-vingt-dix-neuf");
    		}
    		BOOST_TEST(convert(100) == u8"cent");
    		BOOST_TEST(convert(101) == u8"cent un");
    		BOOST_TEST(convert(199) == (commeAuQuebec ? u8"cent nonante-neuf"
    		                                          : u8"cent quatre-vingt-dix-neuf"));
    		BOOST_TEST(convert(200) == (adjNumOrdinal ? u8"deux cent" : u8"deux cents"));
    		BOOST_TEST(convert(201) == u8"deux cent un");
    		BOOST_TEST(convert(900) == (adjNumOrdinal ? u8"neuf cent" : u8"neuf cents"));
    		BOOST_TEST(convert(980) == (commeAuQuebec ? u8"neuf cent octante" :
    		                            adjNumOrdinal ? u8"neuf cent quatre-vingt" :
    		                                            u8"neuf cent quatre-vingts"));
    		BOOST_TEST(convert(999) == (commeAuQuebec ? u8"neuf cent nonante-neuf" :
    		                                            u8"neuf cent quatre-vingt-dix-neuf"));
    		BOOST_TEST(convert(1000) == u8"mille");
    		BOOST_TEST(convert(1001) == u8"mille un");
    		BOOST_TEST(convert(1999) == (commeAuQuebec ? u8"mille neuf cent nonante-neuf" :
    		                                             u8"mille neuf cent quatre-vingt-dix-neuf"));
    		BOOST_TEST(convert(2000) == u8"deux mille");
    		BOOST_TEST(convert(2001) == u8"deux mille un");
    		BOOST_TEST(convert(900'900L) == (adjNumOrdinal ? u8"neuf cent mille neuf cent" :
    		                                                 u8"neuf cent mille neuf cents"));
    		BOOST_TEST(convert(1'000'000L) == u8"un million");
    		BOOST_TEST(convert(1'000'001L) == u8"un million un");
    		BOOST_TEST(convert(1'001'000L) == u8"un million mille");
    		BOOST_TEST(convert(1'001'001L) == u8"un million mille un");
    		BOOST_TEST(convert(2'000'000L) == u8"deux millions");
    		BOOST_TEST(convert(2'000'001L) == u8"deux millions un");
    		BOOST_TEST(convert(200'200'200L) == (
    			adjNumOrdinal ? u8"deux cents millions deux cent mille deux cent" :
    			                u8"deux cents millions deux cent mille deux cents"
    		));
    		BOOST_TEST(convert(1'000'000'000L) == u8"un milliard");
    		BOOST_TEST(convert(1'000'000'001L) == u8"un milliard un");
    		BOOST_TEST(convert(1'000'001'000L) == u8"un milliard mille");
    		BOOST_TEST(convert(1'001'000'000L) == u8"un milliard un million");
    		BOOST_TEST(convert(2'000'000'000L) == u8"deux milliards");
    		BOOST_TEST(convert(2'000'000'001L) == u8"deux milliards un");
    		BOOST_TEST(convert(1'000'000'000'000UL) == u8"un billion");
    		BOOST_TEST(convert(1'000'000'000'000'000UL) == u8"un billiard");
    		BOOST_TEST(convert(1'000'000'000'000'000'000UL) == u8"un trillion");
    	}
    	}
    	}
    	}
    }
     
    BOOST_AUTO_TEST_CASE(Test_nombresLimites)
    {
    	for(bool adjNumOrdinal : {true, false}) {
    	BOOST_TEST_CONTEXT("adjNumOrdinal == " << (adjNumOrdinal ? "true" : "false")) {
     
    		denav::ScripteurNombreFrancais scripteur{};
    		if(adjNumOrdinal)
    			scripteur.modeAdjectifNumeralOrdinal();
     
    		auto const convert = [&scripteur](auto nombre) {
    			return scripteur.versUtf8(nombre);
    		};
     
    // Si un type entier de 32 bits existe...
    #if INT_LEAST32_MAX == INTMAX_C(2'147'483'647)
     
    		static_assert(std::numeric_limits<int32_t>::min() == INT32_C(-2'147'483'648));
    		BOOST_TEST(convert(std::numeric_limits<int32_t>::min()) ==
    			u8"moins deux milliards cent quarante-sept millions "
    			u8"quatre cent quatre-vingt-trois mille six cent quarante-huit");
     
    		static_assert(std::numeric_limits<int32_t>::max() == INT32_C(2'147'483'647));
    		BOOST_TEST(convert(std::numeric_limits<int32_t>::max()) ==
    			u8"deux milliards cent quarante-sept millions "
    			u8"quatre cent quatre-vingt-trois mille six cent quarante-sept");
     
    		static_assert(std::numeric_limits<uint32_t>::max() == UINT32_C(4'294'967'295));
    		BOOST_TEST(convert(std::numeric_limits<uint32_t>::max()) ==
    			u8"quatre milliards deux cent quatre-vingt-quatorze millions "
    			u8"neuf cent soixante-sept mille deux cent quatre-vingt-quinze");
     
    #endif
     
    // Si un type entier de 64 bits existe...
    #if INT_LEAST64_MAX == INTMAX_C(9'223'372'036'854'775'807)
     
    //		static_assert(std::numeric_limits<int64_t>::min() == INT64_C(-9'223'372'036'854'775'808));
    			// Erreur de GCC 7.1.0 : "integer constant is so large that it is unsigned"
     
    		BOOST_TEST(convert(std::numeric_limits<int64_t>::min()) ==
    			u8"moins neuf trillions "
    			u8"deux cent vingt-trois billiards trois cent soixante-douze billions "
    			u8"trente-six milliards huit cent cinquante-quatre millions "
    			u8"sept cent soixante-quinze mille huit cent huit");
     
    		static_assert(std::numeric_limits<int64_t>::max() == INT64_C(9'223'372'036'854'775'807));
    		BOOST_TEST(convert(std::numeric_limits<int64_t>::max()) ==
    			u8"neuf trillions "
    			u8"deux cent vingt-trois billiards trois cent soixante-douze billions "
    			u8"trente-six milliards huit cent cinquante-quatre millions "
    			u8"sept cent soixante-quinze mille huit cent sept");
     
     
    		static_assert(std::numeric_limits<uint64_t>::max() == UINT64_C(18'446'744'073'709'551'615));
    		BOOST_TEST(convert(std::numeric_limits<uint64_t>::max()) ==
    			u8"dix-huit trillions "
    			u8"quatre cent quarante-six billiards sept cent quarante-quatre billions "
    			u8"soixante-treize milliards sept cent neuf millions "
    			u8"cinq cent cinquante et un mille six cent quinze");
     
    #endif
     
    	}
    	}
    }
     
    BOOST_AUTO_TEST_SUITE_END()

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Convertir des chiffres en lettres
    Par azde7015 dans le forum VBA Access
    Réponses: 16
    Dernier message: 19/05/2019, 15h16
  2. convertir les chiffres en lettres
    Par kazannova dans le forum Macros et VBA Excel
    Réponses: 1
    Dernier message: 11/06/2008, 00h05
  3. convertir les chiffres en lettre automatique
    Par baybaymed dans le forum Excel
    Réponses: 9
    Dernier message: 04/06/2008, 09h41
  4. [WD10] Code pour convertir les chiffres en lettres
    Par w-cobra dans le forum WinDev
    Réponses: 10
    Dernier message: 24/01/2007, 16h56
  5. possible convertir un chiffre en lettre avec builder ?
    Par devlopassion dans le forum C++Builder
    Réponses: 8
    Dernier message: 11/09/2006, 17h24

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo