Page suivante Page précédente   Table des matières  

8. Quelques résultats sur la précision des nombres flottants sous Linux

Par Alan Ward award@mypic.ad

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.

8.1 Fonctions de May

Note du traducteur :
Le SGML, à ma connaissance, ne permet pas les indices/exposants.
Dans ce qui suit un indice sera symbolisé par un `_'.
Donc, x indice k sera symbolisé par x_k.

Ces fonctions construisent une série de termes de la forme :

x_0 appartient à [0,1]
x_(k+1) = mu . x_k . (1 - x_k) où mu est un paramêtre

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 x_0 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 x_0 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.

8.2 Notre exemple

Dans cet exemple je propose d'utiliser les valeurs suivantes :

mu = 3,8
x_0 = 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.

Programmation en Turbo-Pascal

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 :

Par exemple, sur un 386 un flottant de 80 bits est codé par :

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 x_30, alors que le format réel s'en tire jusqu'à environ x_60 et que le format double n'est pas encore complètement faux à x_90. 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'à x_100. L'expérience me fait dire qu'il va probablement diverger aux alentours de x_110.

p2c sous Linux

Le programme ci-dessus peut-être compilé pratiquement sans changement en utilisant le programme de traduction p2c disponible sous Linux :

p2c caos.pas

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 x_80.

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.

gcc sous Linux

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);
  }
}

Java

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 :

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);
            }
        }
    }

}

Pour en savoir plus

An introduction to Chaotic Dynamical Systems, R.L. Devaney
Jurassic Park I and II, Michael Crichton (les livres, pas les films !)
The Intel 386-SX microprocessor data sheet, Intel Corp. (disponible à http://developer.intel.com).

Copyright 1999, Alan Ward. Paru dans le numéro 53 de la Linux Gazette de Mai 2000.

Traduction française de Xavier Serpaggi.


Page suivante Page précédente   Table des matières