Facebook EmaiInACirclel
Développement front-end, back-end

On va faire court 3, la représentation digitale des nombres

PentaGuy
PentaGuy
Blogger

En lisant le titre de l’article, vous allez soit vous demander ce que cet article vient faire ici entre la poire et le fromage soit vous dire que je vais enfoncer les portes ouvertes avec des trucs connus et archiconnus. Et vous n’aurez peut-être pas tort mais restez quand même.

Pourquoi j’ai voulu écrire ce petit papier ? Parce que déjà manipuler des nombres avec un ordinateur n’a rien de trivial et que les applications nécessitant du calcul se sont répandues et intéressent pas mal de monde en dehors du cercle restreint des scientifiques et des financiers. Si vous êtes dans la position de quelqu’un qui doit valider une solution de machine learning appliqué et que votre formation vous a tenu très loin de tout ce qui pourrait ressembler de près ou de loin à un problème d’analyse numérique, cet article vous aidera. Ou pas.

Il existe des nombres qu’un ordinateur sait très bien manipuler et qui sont toujours très utiles : les nombres entiers. Ces nombres là servent à compter des objets distincts : ce sont les cardinaux que tout le monde emploie, un, deux, trois, etc. Et tout nombre entier possède une représentation exacte et unique dans le « langage » d’un ordinateur, la base 2. Si je prends 11 par exemple, 11 s’écrit 8 + 2 + 1 et 8, 2 et 1 sont des « puissances » de 2 (23, 21 et 20). En base 2, 11 s’écrit alors 1011, le zéro qui apparaît correspond à la puissance de 2 qui ne participe pas au résultat soit 4 = 22. Pareil pour 14 = 8 + 4 + 2 = (1110). J’ai pris volontairement des petits entiers pour que les exemples soient simples mais avec 64 bits de capacité, un ordinateur est capable de représenter des entiers aussi grands que 264 – 1 (un entier décimal de 20 chiffres). Le nombre minimal de bits nécessaires pour représentation un nombre entier en binaire est ce qu’on appelle la précision. On peut même aller plus loin avec les entiers. Un langage de programmation comme Python permet de manipuler des entiers de précision arbitraire.

Qu’en est-il des nombres autres que les entiers ? C’est là que les choses se corsent un peu. On ne considère que les nombres décimaux et on compare le résultat des deux calculs suivants en Python (par exemple) : (0.999+0.0004)+0.0004 et 0.999+(0.0004+0.0004). Là, vous vous dites que le résultat de chaque calcul doit être identique à l’autre (si vous êtes mathématiquement cultivé vous pouvez même dire que l’addition est associative). Eh bien non, on constate une différence qui tient à ce que les nombres décimaux sont représentés de façon approximative par un ordinateur comme une somme de fractions binaires (ce qu’on appelle un flottant ou float en anglais). Et certains nombres décimaux n’ont pas de représentation binaire exacte de la manière que certaines fractions n’ont pas de représentation décimale exacte. Prenons un exemple, si je veux écrire une représentation décimale de 1/3, je peux écrire 0.3, 0.33, 0.3333 et ainsi de suite jusqu’à un niveau arbitraire de précision. Cependant, le nombre décimal obtenu ne sera jamais égal exactement à 1/3. En binaire, c’est la même chose mais c’est plus déroutant car nous sommes habitués à la notation décimale. Si je veux exprimer le décimal 0.1 (1/10) en binaire comme somme de fractions des puissances de 2, j’obtiens aussi un développement infini.

En règle générale, cette approximation est totalement indolore sauf dans deux cas : le calcul décimal exact et le calcul de haute précision qui vont, respectivement, intéresser le financier et le data scientist.

Calculer de façon décimale avec exactitude c’est contourner les limitations de la représentation des nombres décimaux sous forme de fraction en base 2 pour ne pas être obligé d’utiliser des fonctions d’arrondi et des fonctions de formatage pour maîtriser la précision des calculs et les entrées/sorties. Et un moyen simple, c’est d’utiliser comme représentation interne des nombres les fractions décimales !

Cette solution présente ses propres inconvénients puisque l’ensemble des nombres décimaux est assez restrictif en ce qui concerne la division : elle n’y est jamais exacte ! Enfin, elle est aussi exacte que la division entière (si, si, celle des entiers avec quotient et reste entiers) puisqu’il s’agit de la même opération.

Il ne surprendra personne que les calculs financiers ont besoin d’exactitude. On a vu que même la somme de flottants était problématique, or, les calculs financiers (ou comptables plutôt) sont des additions (et soustractions). Les seules autres opérations simples et courantes sont la multiplication par une fraction décimale (un pourcentage). Dans ces conditions, posséder un modèle de représentation numérique exact est un atout qui permet de s’abstraire de la dépendance envers les flottants et leur imprécision. Et le fait de ne pas disposer d’une division exacte n’est finalement pas si embêtant que ça.

Le calcul de haute précision c’est encore une autre paire de manches. Il s’agit souvent d’utiliser des algorithmes d’approximation numériques pour trouver des solutions à des problèmes cocasses comme inverser une matrice, trouver la valeur approchée de l’intégrale d’une fonction ou celle des racines d’un polynôme. Ne riez pas, tous les algorithmes qui vous rendent la vie plus facile (ou moins libre c’est selon) sont conçus sur la base de ces problèmes de mathématiques appliquées.

Lors de ces calculs les erreurs de représentation que nous avons expliquées plus haut s’accumulent au fur et à mesure des calculs et conduisent les algorithmes à faire n’importe quoi, ce n’importe quoi conduisant soit à un résultat très éloigné du résultat attendu soit à ce que l’algorithme ne s’arrête pas (il existe d’autres causes au comportement pathologique des algorithmes numériques comme le conditionnement).

Comment fait-on pour éviter cela ? Comme dans le cas du calcul décimal, on cherche une meilleure façon de représenter les nombres. Et pour ça, on dispose de deux stratégies.

La première stratégie est de représenter les nombres réels (oui les calculs mathématiques utilisent des nombres réels) par des entiers, un entier multiplié par une puissance de 10 , ainsi π pourra être représenté par (3141592653589793, -15) ou (314159, -5). C’est, bien entendu, une représentation décimale qui ne corrige pas du tout le problème d’arrondi du calcul des divisions et des fonctions usuelles.

La seconde stratégie est de passer par le calcul symbolique dans lequel les entiers sont représentés par des entiers « classiques », les rationnels par des fractions d’entiers (comme 1/3) et le reste des réels comme les images de rationnels par des fonctions usuelles (comme √2 par exemple), les constantes mathématiques comme π et e conservent, elles, leur symbole usuel. Le calcul symbolique n’utilise donc que des symboles et donne une valeur approchée du résultat du calcul qu’à la toute fin.

En conclusion, l’impact d’une représentation numérique n’est pas neutre sur les applications. L’inconvénient de ces représentations de nombres (décimal, symbolique) est qu’elles sont lentes par rapport à la représentation binaire. Mais elles permettent plus de précision et autorisent les développeurs à écrire un code plus concis et plus clair par l’abstraction qu’elles réalisent.


Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *