Copyright © 2000 Alan Ward
Copyright © 2000 Xavier Serpaggi
Article paru dans le n°53 de la Gazette Linux de mai 2000.
Cet article est publié selon les termes de la Open Publication License. La Linux Gazette n'est ni produite, ni sponsorisée, ni avalisée par notre hébergeur principal, SSC, Inc.
Table des matières
Résumé
Dans cet article je vous propose une découverte expérimentale du comportement de Linux quand il effectue des calculs en simple ou double précision. J'utilise une fonction chaotique pour montrer combien peut varier le calcul résultant d'un même programme selon qu'il est exécuté sous Linux ou sous un système d'exploitation de Microsoft.
Les publics concernés sont les étudiants en math et physique ainsi que les professeurs, bien que les équations mises en jeu soient accessibles à pratiquement tout le monde.
J'utilise les langages Pascal, C et Java puisque ce sont ceux-là qui sont le plus répandus de nos jours.
Ce papier s'intéresse plus particulièrement aux architectures Intel. Les concepts de base sont les mêmes pour d'autres types de processeurs bien que certains détails puissent changer.
Ces fonctions construisent une série de termes de la forme :
x0 appartient à [0,1] x(k+1) = mu . xk . (1 - xk) où mu est un paramêtre
Pour 0 <= mu < 3, le comportement des séries est déterministe.
Pour 3 <= mu < 4, le comportement est chaotique.
En simplifiant un peu, la différence entre un système chaotique et déterministe est leur sensibilité aux conditions initiales. Un système chaotique est très sensible : une petite variation des valeurs de départ de x0 conduira a des différences croissantes dans les termes suivants. Donc, n'importe quelle erreur qui rôde dans les calculs — comme un manque de précision — donnera éventuellement naissance à des résultats très différents.
D'autres exemples de systèmes chaotiques sont les orbites des satellites et les prévisions météorologiques.
D'un autre côté, un système déterministe n'est pas si sensible. Une petite erreur dans x0 nous ammènera à calculer des termes qui, bien que différents des valeurs exactes, en seront « suffisamment approchés » (quoi que cela puisse signifier).
Un exemple de système déterministe est la trajectoire d'une balle de ping-pong.
Donc, les fonctions chaotiques sont utiles pour tester la précision des calculs de différents systèmes et de différents compilateurs.
Dans cet exemple je propose d'utiliser les valeurs suivantes :
mu = 3,8 x0 = 0,5
Un calcul précis avec un module spécial permettant une précision de 1000 chiffres donne les résultats suivants :
k x(k) ----- --------- 10 0.18509 20 0.23963 30 0.90200 40 0.82492 50 0.53713 60 0.66878 70 0.53202 80 0.93275 90 0.79885 100 0.23161
Comme vous pouvez le voir, les séries fluctuent joyeusement entre 0 et 1.
Un programme pour calculer cette fonction s'écrit simplement en Turbo Pascal pour MS-DOS : version texte.
program caos; {$n+} { vous devez activer le calcul materiel des flottants pour pouvoir utiliser le type etendu } uses crt; var s : single; { 32-bit real } r : real; { 48-bit real } d : double; { 64-bit real } e : extended; { 80-bit real } i : integer; begin clrscr; s := 0.5; r := 0.5; d := 0.5; e := 0.5; for i := 1 to 100 do begin s := 3.8 * s * (1 - s); r := 3.8 * r * (1 - r); d := 3.8 * d * (1 - d); e := 3.8 * e * (1 - e); if (i/10 = int(i/10)) then begin writeln (i:10, s:16:5, r:16:5, d:16:5, e:16:5); end; end; readln; end.
Comme vous pouvez le voir, Turbo Pascal a un certain nombre de types de flottants, chacun sur un nombre différent de bits. Dans chaque cas des bits sont réservés pour :
le signe : un bit indique une valeur positive ou négative,
la magnitude (ou mantisse) : le nombre lui-même codé en binaire,
l'exposant : la puissance de 2 par laquelle il faut multiplier la mantisse pour obtenir la vraie valeur du nombre. Remarquez que ça peut très bien être négatif.
Par exemple, sur un 386 un flottant de 80 bits est codé par :
bits 0 à 55 : la mantisse,
bits 56 à 78 : l'exposant,
bit 79 : le signe.
Naturellement, le codage des flottants à l'intérieur de la machine est déterminé par le fabricant. Cependant, le compilateur peut spécifier des codages différents pour des calculs internes. Si l'émulation des flottants n'est pas utilisée, le compilateur doit fournir un moyen de transformer les codages du compilateur au matériel. C'est le cas du Turbo Pascal.
Les résultats du programme ci-dessus sont :
k single real double extended ---- --------- --------- --------- ---------- 10 0.18510 0.18510 0.18510 0.18510 20 0.23951 0.23963 0.23963 0.23963 30 0.88423 0.90200 0.90200 0.90200 40 0.23013 0.82492 0.82493 0.82493 50 0.76654 0.53751 0.53714 0.53714 60 0.42039 0.64771 0.66878 0.66879 70 0.93075 0.57290 0.53190 0.53203 80 0.28754 0.72695 0.93557 0.93275 90 0.82584 0.39954 0.69203 0.79884 100 0.38775 0.48231 0.41983 0.23138
Dans tous les cas, les premiers termes sont assez proches, étant donné que les pertes de précision (par troncature) dues aux calculs intenses n'ont jamais eu lieu. Il en résulte que le format le moins précis (single) perd tout contact avec la réalité aux alentours de x30, alors que le format réel s'en tire jusqu'à environ x60 et que le format double n'est pas encore complètement faux à x90. Ce sont tous des codages flottants du compilateur.
Le format étendu — qui est le format de codage natif du matériel — garde une précision suffisante jusqu'à x100. L'expérience me fait dire qu'il va probablement diverger aux alentours de x110.
Le programme ci-dessus peut-être compilé pratiquement sans changement en utilisant le programme de traduction p2c disponible sous Linux :
converti caos.pas
en coas.c
cc caos.c -lp2c -o caos
compile coas.c
avec la bibliothèque p2c en utilisant gcc
Les résultats deviennent :
k single real double extended ---- --------- --------- --------- ---------- 10 0.18510 0.18510 0.18510 0.18510 20 0.23951 0.23963 0.23963 0.23963 30 0.88423 0.90200 0.90200 0.90200 40 0.23013 0.82493 0.82493 0.82493 50 0.76654 0.53714 0.53714 0.53714 60 0.42039 0.66878 0.66878 0.66878 70 0.93075 0.53190 0.53190 0.53190 80 0.28754 0.93558 0.93558 0.93558 90 0.82584 0.69174 0.69174 0.69174 100 0.38775 0.49565 0.49565 0.49565
Il est intéressant de noter que le traducteur p2c convertit les nombres
flottants simple précision du Pascal en simple précision du C, alors que les
types real
, double
et extended
sont tous transformés en
type double
du C.
C'est le format qui garde sa précision jusqu'aux alentours de x80.
Je ne dispose pas de données pour prouver le bien fondé de ce qui suit, mais mon
impression est que le type C double
de codage des flottants se fait également
sur 64 bits, mais avec une répartition mantisse/exposant différente de celle du
Turbo Pascal.
Le programme ci-dessus, réécrit en C et compilé avec gcc donne, de manière assez naturelle, exactement les mêmes résultats qu'avec p2c :
#include <stdio.h> int main() { float f; double d; int i; f = 0.5; d = 0.5; for (i = 1; i <= 100; i++) { f = 3.8 * f * (1 - f); d = 3.8 * d * (1 - d); if (i % 10 == 0) printf ("%10d %20.5f %20.5f\n", i, f, d); } }
Le langage de programmation Java est un cas complètement à part, étant donné que, dès le départ, il a été conçu pour fonctionner sur des plateformes aussi nombreuses que différentes.
Un fichier Java .class
contient le code source du programme compilé dans le langage
d'une Machine Virtuelle.
Ce fichier « exécutable » est alors interprété sur un système client par le
premier interprête Java disponible.
Cependant, dans les spécifications Java, les problèmes de précision sur les flottants sont pris très au sérieux. Chaque interprête Java devrait obtenir exactement les mêmes résultats sur des calculs en simple ou double précision.
Ceci signifie qu'un même programme devra :
être exécuté avec la même précision sur différentes architectures (par exemple Intel, Motorola, Alpha, ...),
être exécuté avec la même précision sur la même architecture, même si l'interprête Java est différent.
Le lecteur peut aisément expérimenter cela. L'applet suivante calcule les séries de May dont nous avons parlé. Comparez ses résultats sur votre propre configuration en l'exécutant avec Netscape, HotJava, appletviewer, etc... Vous pouvez également comparez avec les mêmes navigateurs, ou d'autres, sous Windoze. Ouvrez simplement cette page avec chaque navigateur.
Jusqu'à présent je n'ai trouvé qu'une seule exception à cette règle. Vous devinez qui ? Microsoft Explorer 3.0 !
Pour terminer, voilà le code source de l'applet Java :
import java.applet.Applet; import java.lang.String; import java.awt.*; public class caos extends Applet { public void paint (Graphics g) { float f; double d; String s; int i, y; f = (float)0.5; d = 0.5; g.setColor (Color.black); g.drawString ("k", 10, 10); g.drawString ("float", 50, 10); g.drawString ("double", 150, 10); g.setColor (Color.red); y = 20; for (i = 1; i <= 100; i++) { f = (float)3.8* f * ((float)1.0 - f); d = 3.8 * d * (1.0 - d); if (i % 10 == 0) { y += 12; g.drawString (java.lang.String.valueOf(i), 10, y); g.drawString (java.lang.String.valueOf(f), 50, y); g.drawString (java.lang.String.valueOf(d), 150, y); } } } }
The Intel 386-SX microprocessor data sheet. Intel Corp.. http://developer.intel.com.
L'adaptation française de ce document a été réalisée dans le cadre du Projet de traduction de la Gazette Linux.
Vous pourrez lire d'autres articles traduits et en apprendre plus sur ce projet en visitant notre site : http://www.traduc.org/Gazette_Linux.
Si vous souhaitez apporter votre contribution, n'hésitez pas à nous rejoindre, nous serons heureux de vous accueillir.