Aller au contenu

2.8 Codage des non-entiers⚓︎

image

image

Le principe est l'extension du système déjà rencontré pour les nombres entiers. La partie décimale (à droite de la virgule) correspondra aux puissances négatives de 2.

... 8 4 2 1 0.5 0.25 0.125 ...
... \(2^3\) \(2^2\) \(2^1\) \(2^0\) \(2^{-1}\) \(2^{-2}\) \(2^{-3}\) ...
... 0 1 1 0, 1 0 1 ...

Exemple : \(110,101_2=1 \times 2^2 + 1 \times2^1 +0 \times 2^0 + 1 \times 2^{-1} +0 \times 2^{-2}+1 \times 2^{-2} =4+2+0,5+0,125=6,625\)

1. Tentatives de conversion⚓︎

1.1 Théorème de décomposition en puissances de 2⚓︎

Tout commence bien, avec un résultat mathématique rassurant : tous les nombres réels peuvent s'écrire comme une somme de puissances de 2 (puissances positives et négatives).

Théorème

Pour tout réel \(x \in \mathbb{R}^+\), il existe \(p \in \mathbb{N}\) et \((a_p,a_{p-1},...,a_0,a_{-1},a_{-2},...)\) tels que \(x = \sum_{i=0}^pa_i2^i+\sum_{i=1}^{+\infty}a_{-i}2^{-i}\)

Écrire un nombre en binaire revient à calculer les coefficients \(a_k\) (ils sont égaux à 0 ou 1). Il y en a un nombre fini pour la partie entière, mais un nombre potentiellement infini pour la partie décimale.

1.2 Méthode de conversion⚓︎

Considérons le nombre \(3,6875\). Il se décompose en une partie entière (3) et une partie décimale (\(0,6875\)).

  • partie entière : \(3=11_2\)
  • partie décimale : la conversion de \(0,6875\) se fait en plusieurs étapes.
    \(0,6875 \times 2 = \textbf{1},375\)
    \(0,375 \times 2 = \textbf{0},75\)
    \(0,75 \times 2 = \textbf{1},5\)
    \(0,5 \times 2 = \textbf{1}\)

On prend ensuite le chiffre des unités de tous les nombres obtenus : 1011

Donc \(3,6875=11,1011_2\)

Exercice 1

Donner l'écriture binaire de 20,875.

Correction
  • partie entière : \(20 = 10100_2\)
  • partie décimale :
    • \(0,875 \times 2 = \textbf{1},75\)
    • \(0,75 \times 2 = \textbf{1},5\)
    • \(0,5 \times 2 = \textbf{1}\)

Donc \(20,875=10100,111_2\)

Exercice 2

Donner l'écriture binaire de 0,2.

Correction
  • partie entière : \(0 = 0_2\)
  • partie décimale :
    • \(0,2 \times 2 = \textbf{0},4\)
    • \(0,4 \times 2 = \textbf{0},8\)
    • \(0,8 \times 2 = \textbf{1},6\)
    • \(0,6 \times 2 = \textbf{1},2\)
    • \(0,2 \times 2 = \textbf{0},4\)
    • et cela continue...

Le nombre 0,2 n'admet pas d'écriture binaire finie. 😦

Conclusion⚓︎

Certains nombres n'admettent pas une écriture binaire finie. Or la mémoire d'un ordinateur, quelqu'il soit, est toujours finie. Certains nombres ne peuvent donc pas être représentés correctement en machine : c'est une impossibilité théorique. Cela amène à des comportements étranges :

>>> 0.1 + 0.2
0.30000000000000004

Remarque : parmi les nombres décimaux à un chiffre après la virgule (0,1 0,2 0,3 ...) seul 0,5 admet une écriture binaire finie ! Tous les autres ont une représentation en machine qui n'en donne qu'une valeur approchée. Vous pouvez avoir un aperçu de la représentation des nombres flottants sur ce site.

2. Conséquences : la difficile manipulation des flottants⚓︎

En python, les nombres non entiers sont du type float.

>>> type(0.1)
<class 'float'>

Ces flottants (traduction française) sont à manipuler avec une extrême précaution. Il faut garder en tête que les calculs sont potentiellement faux, du moins imprécis, lorsque des flottants interviennent.

>>> 0.5 - 0.2 - 0.2 - 0.1
-2.7755575615628914e-17

➡ En 1991, durant la Guerre du Golfe, un missile anti-missile américain a raté sa cible de 500 mètres car son ordinateur interne émettait un signal toutes les 0.1 secondes. Au bout de 100 heures de fonctionnement, l'approximation du nombre flottant 0.1 a conduit à un décalage de 0,34 secondes, ce qui lui a fait rater sa cible. (source)

➡ Hormis ce cas tragique, est-ce si grave de devoir travailler avec des valeurs approchées ? Non, pas vraiment : par exemple, la valeur de Pi utilisée par la NASA pour faire ses calculs (généralement très, très, très précis) est 3.141592653589793, donc «juste» 15 chiffres après la virgule. (source)

➡ Un bug intéressant : on a enfin compris pourquoi dans Minecraft les barques tombant d'une certaine hauteur cassaient, et pas d'autres. La raison ? Les nombres flottants bien sûr ! (vidéo)

3. Comment faire des tests d'egalité sur les flottants ?⚓︎

Première réponse : ON N'EN FAIT PAS.

Si a et b sont deux flottants, le test classique

if a == b :
    print("a et b sont égaux")

a de grandes chances d'échouer :

Le script

1
2
3
4
5
6
a = 0.1
b = 0.3 - 0.2
if a == b :
    print("a et b sont égaux")
else :
    print("a et b sont différents")

renverra

a et b sont différents

Si vraiment un test d'égalité est nécessaire, on ne va pas tester l'égalité entre a et b mais leur proximité, grâce à la valeur absolue de leur différence.

La fonction abs(a-b) renvoie un nombre positif égal à la distance entre a et b. Il faut alors décider d'un écart minimal e en dessous duquel on considèrera que a et b sont égaux.

Le script

a = 0.1
b = 0.3 - 0.2
e = 10**(-12)
if abs(a - b) < e :
    print("a et b sont égaux")
else :
    print("a et b sont différents")

renverra

a et b sont égaux
car notre code les aura considérés comme «suffisamment proches».

Exercice 3

On considère la fonction \(f(x)=x^3-6x+2\).
L'équation \(f(x)=1\) admet une solution unique dans l'intervalle \([0;1]\).
Trouver une valeur approchée de cette solution à \(10^{-5}\) près. On prendra e\(=0,001\).

Correction
def f(x):
    return x**3 - 6*x + 2

e = 10**(-3)
a = 0
while abs(f(a) - 1 ) > e :
    a += 10**(-5)
print(a)