Formatage des nombres
Le formatage des nombres sous forme de chaines de caractères en vue de leur affichage est une fonctionnalité clé quand on développe des applications à vocations techniques ou scientifiques. Les languages comme le C++ disposent de beaucoup de possibilités pour fournir cette fonctionnalité. Et la librairie de Qt en rajoute encore une couche. Cela dit, lorsque l'on est un scientifique ou un ingénieur, on a ses petites habitudes de professionnel... et dans ce cas, il peut y avoir quelques limites pas toujours simples à régler avec ces fonctions de formatage de nombres. Deux exemples...
Un truc que l'on fait souvent dans une application est de simuler un afficheur numérique d'un instrument de mesure. Ces afficheurs ont de bonnes habitudes. Quelque soit la mesure qu'ils affichent, le nombre est toujours formatté de la même manière, le nombre de chiffres significatifs avant ou après le séparateur décimal ne change pas, le séparateur décimal ne bouge pas, le signe non plus, les exposants encore moins, etc... Cela rend la lecture de la mesure plus rapide et plus aisée. C'est un comportement assez difficile à obtenir avec les routines de formatage des nombres des languages informatiques. Comme ce formatage est essentiellment guidé par le chiffre lui même, on observe souvent des glissements de décades, etc... Bref, obtenir un affichage stable, et cela quelque soit le nombre affiché peut conduire à de sacrées difficultés de formatage avec notre bon vieux printf.
Mais la chose la plus frustrante est que ces fonctions de formatage ne respectent pas la norme ISO des décades. Un ingénieur ne dira jamais "J'ai mesuré 12e10-5 Ampère". Il dira "J'ai mesuré 120 µ Ampère". Cela correspond à 120e10-6. Quand on parle de mesures physiques, on ne parle que selon ces echelles de décades ISO, des multiples de 3 : Kilo, Méga, Giga, etc... et dans l'autre sens : milli, micro, nano, pico, etc... Cela correspond à 10e3, 10e6, 10e9... 10e-3, 10e-6, 10e-9, 10e-12. La norme ISO définie toutes ces décades de 10e-24 à 10e24. Et elles ont toutes un petit nom :
| Exposant | Nom | Préfixe d'unité |
| -24 | Yocto | y |
| -21 | Zepto | z |
| -18 | Atto | a |
| -15 | Femto | f |
| -12 | Pico | p |
| -9 | Nano | n |
| -6 | Micro | µ |
| -3 | Milli | m |
| 0 | ||
| 3 | Kilo | K |
| 6 | Mega | M |
| 9 | Giga | G |
| 12 | Tera | T |
| 15 | Peta | P |
| 18 | Exa | E |
| 21 | Zetta | Z |
| 24 | Yotta | Y |
Essayez donc de demander à votre language de formater vos nombres uniquement selon cette echelle... Quand à moi, je n'ai jamais trouvé de solution élégante.
Pour formater les nombres vous pouvez utiliser la classe TNumberCruncher ou le widget TNumber.
Comment ça marche ?
La classe expose 13 propriétés qui définissent le formatage :| Propriété | Type | Signification succincte |
| DigitsAfterDecimalSeparator | int | Nombres de chiffres après le séparateur décimal |
| DigitsBeforeDecimalSeparator | int | Nombres de chiffres avant le séparateur décimal |
| DigitsForIntegers | int | Nombres de chiffres significatifs pour les nombres entiers |
| EnableGroupSeparator | bool | Activation de la séparation par groupes de 3 chiffres |
| ForceCLocal | bool | Forçage du point comme séparateur décimal |
| ForceSign | bool | Forçage du signe |
| MessageOverflow | QString | Message de dépassement de gamme |
| NumberFormat | TXS::NumberFormat | Directive de formatage principale |
| PadCharacter | QChar | Caractère de remplissage |
| ShowXOB | bool | Affichage du préfixe pour les bases binaire, octodécimale, hexadécimale |
| TimeFormat | TXS::TimeFormat | Directive de formatage des dates et heures |
| UpperXOB | bool | Préfixe de base binaire, ocotdécimale, hexa décimale en majuscules |
| XOBFormat | TXS::XOBFormat | Formatage des entiers en base binaire, octodécimale, hexadécimale |
Ces propriétés ont ou n'ont pas de significaiton selon le type de valeur à formater et selon la valeur attribuée à la propriété NumberFormat.
On distingue 3 cas possibles :
La valeur à formater est une date et/ou une heure :
Dans ce cas, la propriété NumberFormat prend la valeur TXS::NumberFormat_DateTime. Le format de date et/ou heure est alors uniquement défini par la propriété TimeFormat.
La valeur à formater est un nombre entier :
Le nombre peut être codé de 8 à 64 bit. La propriété NumberFormat peut alors prendre plusieurs valeurs. Le tableau suivant résume l'impact des autres propriétés sur le formatage en fonction de la valeur de la propriété NumberFormat :

Les quelques règles suivantes s'appliquent :
- Les formats TXS::NumberFormat_Integer_AutoDecimal et TXS::NumberFormat_Integer_Decimal peuvent traiter des nombres signés ou non. Les autres formats cités dans le tableau considèrent que la valeur est un entier non signé.
- Les formats automatiques définissent eux mêmes le nombre de chiffres significatifs.
- Pour les autres formats non automatiques, le nombre de chiffres significatifs est spécifié via la propriété DigitsForIntegers. Si cette propriété ne perrmet pas de formater la valeur, la valeur de la propriété MessageOverflow est retournée. Si cette propriété définie plus de chiffres significatifs que nécessaire, le caractère définie par la propriété PadCharacter est ajouté en début de chaine jusqu'à obtenir le nombre de caractères requis.
- Si la valeur à formater n'est pas un entier mais un réel, elle est d'abord "castée" au type qlonglong.
- La fonctionnalité de séparation de groupe via la propriété EnableGroupSeparator ne s'applique qu'au format décimal. Les formats hexadécimal, octodécimal et binaire ont une séparation par groupes de bit via la propriété XOBFormat.
- Lorsque la propriété ForceCLocal est placée à la valeur true, la propriété EnableGroupSeparator n'a plus d'influence car le format des nombres en C ne comporte jamais de séparations de groupes.
- Le préfixe pour la base héxadécimale est "0x". Le préfixe pour la base octodécimale est "0o". Le préfixe pour la base binaire est "0b".
- Lors de l'utilisation des formats TXS::NumberFormat_Integer_HexaDecimal, TXS::NumberFormat_Integer_OctoDecimal et TXS::NumberFormat_Integer_Binary, il faut faire attention au fait que pour respecter la notaation par groupe de nibbles, octets ou mots, la librairie peut rajouter des zéros en tête de la valeur produite. Cela peut conduire à un dépassement du nombre de chiffres significatifs précisé par la propriété DigitsForIntegers et donc retourner une valeur hors gamme telle que définie par la propriété MessageOverflow. Il est donc nécessaire de donner à la propriété DigitsForIntegers une valeur adéquate en fonction du format en nibbles, octets ou mots.
Exemples :
Les exemples suivants sont donnés par formats. La valeur à convertir est la même pour tous les formats : +123456789. Dans tous ces exemples, la propriété PadCharacter est réglée à '_', la propriété MessageOverflow est réglée à "OVL".
Format TXS::NumberFormat_Integer_AutoDecimal
| ForceSign | EnableGroupSeparator | ForceCLocal | Résultat |
| true | true | false | +123 456 789 |
| true | false | false | +123456789 |
| false | true | false | 123 456 789 |
| false | true | true | 123456789 |
Format TXS::NumberFormat_Integer_Decimal
| DigitsForIntegers | ForceSign | EnableGroupSeparator | ForceCLocal | Résultat |
| 7 | true | true | false | OVL |
| 9 | true | false | false | +123456789 |
| 12 | true | false | false | +___123456789 |
| 12 | false | true | false | ___123 456 789 |
| 12 | false | true | true | ___123456789 |
Format TXS::NumberFormat_Integer_AutoHexaDecimal
| XBOFormat | ShowXOB | UpperXOB | Résultat |
| XOBFormat_None | false | false | 75bcd15 |
| XOBFormat_Nibble | false | false | 7 5 b c d 1 5 |
| XOBFormat_Byte | true | false | 0x07 5b cd 15 |
| XOBFormat_Word | true | true | 0X075B CD15 |
Format TXS::NumberFormat_Integer_HexaDecimal
| DigitsForIntegers | XBOFormat | ShowXOB | UpperXOB | Résultat |
| 9 | XOBFormat_None | true | false | 0x__75bcd15 |
| 5 | XOBFormat_None | true | false | OVL |
| 9 | XOBFormat_Nibble | true | false | 0x__7 5 b c d 1 5 |
| 9 | XOBFormat_Word | true | true | 0X_075B CD15 |
| 8 | XOBFormat_Word | true | true | 0X075B CD15 |
| 7 | XOBFormat_Word | true | true | OVL |
Format TXS::NumberFormat_Integer_AutoOctoDecimal
| XBOFormat | ShowXOB | UpperXOB | Résultat |
| XOBFormat_None | false | false | 726746425 |
| XOBFormat_Nibble | false | false | 07 26 74 64 25 |
| XOBFormat_Byte | true | false | 0o726 746 425 |
| XOBFormat_Word | true | true | 0O000726 746425 |
Format TXS::NumberFormat_Integer_OctoDecimal
| DigitsForIntegers | XBOFormat | ShowXOB | UpperXOB | Résultat |
| 9 | XOBFormat_None | true | false | 0o726746425 |
| 5 | XOBFormat_None | true | false | OVL |
| 9 | XOBFormat_Nibble | true | false | OVL |
| 9 | XOBFormat_Byte | true | false | 0o726 746 425 |
| 9 | XOBFormat_Word | true | true | OVL |
| 16 | XOBFormat_Word | true | true | 0O____000726 746425 |
Format TXS::NumberFormat_Integer_AutoBinary
| XBOFormat | ShowXOB | UpperXOB | Résultat |
| XOBFormat_None | false | false | 111010110111100110100010101 |
| XOBFormat_Nibble | false | false | 0111 0101 1011 1100 1101 0001 0101 |
| XOBFormat_Byte | true | false | 0b00000111 01011011 11001101 00010101 |
| XOBFormat_Word | true | true | 0B0000011101011011 1100110100010101 |
Format TXS::NumberFormat_Integer_Binary
| Digits... | XBOFormat | ShowXOB | UpperXOB | Résultat |
| 27 | XOBFormat_None | false | false | 111010110111100110100010101 |
| 24 | XOBFormat_None | false | false | OVL |
| 27 | XOBFormat_Nibble | false | false | OVL |
| 28 | XOBFormat_Nibble | false | false | 0111 0101 1011 1100 1101 0001 0101 |
| 27 | XOBFormat_Word | true | false | OVL |
| 32 | XOBFormat_Word | false | false | 0000011101011011 1100110100010101 |
| 35 | XOBFormat_Word | false | false | ___0000011101011011 1100110100010101 |
La valeur à formater est un nombre réel :
Le nombre peut être du type float ou du type double. La propriété NumberFormat peut alors prendre plusieurs valeurs. Le tableau suivant résume l'impact des autres propriétés sur le formatage en fonction de la valeur de la propriété NumberFormat :

Les quelques règles suivantes s'appliquent :
- Le format TXS::NumberFormat_Any_Compact garanti la conversion en chaine de caractères. Les propriétés PadCharacter et MessageOverflow n'ont pas d'influence. Ce format retourne la chaine la plus courte possible quelque soit son format numérique. Ainsi vous ne pouvez pas être assuré que la chaine retournée sera au format à virgule flottante ou au format scientifique avec exposant.
- Les formats TXS::NumberFormat_Double_AutoFixedPoint et TXS::NumberFormat_Double_FixedPoint sont équivalents. La seule différence est que le format TXS::NumberFormat_Double_AutoFixedPoint calcule lui même le nombre de chiffres significatifs avant la virgule pour éviter tout caractères de complément ou le message de dépassement de gamme définis par les propriétés PadCharacter et MessageOverflow. Le revers de la médaille du mode TXS::NumberFormat_Double_AutoFixedPoint est que vous ne pouvez pas être sur de toujours afficher le même nombre de chiffres significatifs avant le séparateur décimal. Généralement, cela ne gêne pas une interprétation simple car le séparateur décimal ne change jamais de position. Dans ces deux formats, si un dépassement de gamme est détecté, le contenu de la propriété MessageOverflow est retournée. Si le nombre de chiffres spécifié avant le séparateur décimal via la propriété DigitsBeforeDecimalSeparator est supérieur au nombre nécessaire le nombre sera précédé du caractère défini pour la propriété PadCharacter. Enfin, si le nombre de chiffres après le séparateur décimal spécifié par la propriété DigitsAfterDecimalSeparator est supérieur au nombre nécessaire, les derniers chiffres seront des zéros.
- Le format TXS::NumberFormat_Double_Scientific fonctionne de manière strictement identique aux formats TXS::NumberFormat_Double_AutoFixedPoint et TXS::NumberFormat_Double_FixedPoint mais il garantit une chaine de caractères retournée sous forme de valeur avec exposant.
- Les formats TXS::NumberFormat_Double_ScientificStandard et TXS::NumberFormat_Double_ScientificRange garantissent une chaine de caractères retournée sous forme de valeur avec exposant. Contrairement au format TXS::NumberFormat_Double_Scientific ces deux formats garantissent de ne retourner que des exposants multiples de 3 respectant ainsi la norme ISO. Le format TXS::NumberFormat_Double_ScientificRange remplace l'exposant par le préfixe d'unité. Pour ces formats la propriété DigitsBeforeDecimalSeparator ne constitue pas une obligation, mais un minimum de chiffres significatifs avant le séparateur décimal. Cela du fait que pour aligner l'exposant sur un multiple de 3, il est possible que le séparateur décimal doivent être déplacé afin de conserver la résolution demandée.
- Lorsque la propriété ForceCLocal est placée à la valeur true, la propriété EnableGroupSeparator n'a plus d'influence car le format des nombres en C ne comporte jamais de séparations de groupes.
Exemples :
Format TXS::NumberFormat_Any_Compact (Valeur test = 1234,56789)
| ForceSign | EnableGroupSeparator | ForceCLocal | Résultat |
| false | true | false | 1 234,57 |
| true | true | false | +1 234,57 |
| false | true | true | 1234,57 |
Format TXS::NumberFormat_Double_AutoFixedPoint (Valeur test = 1234,56789)
| DigitsAfter.. | ForceSign | EnableGroupSeparator | ForceCLocal | Résultat |
| 9 | false | false | true | 1234.567890000 |
| 6 | true | true | false | +1 234,567890 |
Format TXS::NumberFormat_Double_FixedPoint (Valeur test = 123,456789)
| DigitsBefore.. | DigitsAfter.. | ForceSign | EnableGroupSeparator | ForceCLocal | Résultat |
| 9 | 6 | false | false | true | _____1234.567890 |
| 6 | 6 | true | true | false | +__1 234,567890 |
| 2 | 6 | true | true | false | OVL |
Format TXS::NumberFormat_Double_Scientific (Valeur test = 0,000000123456789)
| DigitsBefore.. | DigitsAfter.. | ForceSign | EnableGroupSeparator | ForceCLocal | Résultat |
| 4 | 6 | false | false | false | ___1,234568e-07 |
| 2 | 6 | false | false | true | _1.234568e-07 |
| 4 | 3 | false | false | false | ___1,235-07 |
| 4 | 3 | true | false | false | +___1,235-07 |
| 1 | 5 | true | false | false | +1,23457-07 |
Format TXS::NumberFormat_Double_ScientificStandard (Valeur test = 0,000000123456789)
| DigitsBefore.. | DigitsAfter.. | ForceSign | EnableGroupSeparator | ForceCLocal | Résultat |
| 1 | 5 | true | false | false | +123,45700e-9 |
| 3 | 8 | false | false | false | 123,45678900e-9 |
Format TXS::NumberFormat_Double_ScientificStandard (Valeur test = 0,000000123456789)
| DigitsBefore.. | DigitsAfter.. | ForceSign | EnableGroupSeparator | ForceCLocal | Résultat |
| 1 | 8 | true | false | false | +123,456789000 n |
| 3 | 8 | false | false | false | 123,45678900 n |