Comprendre les nombres à virgule flottante

written by netinfluence 24 septembre 2009
Comprendre les nombres à virgule flottante

1. Représentation binaire des nombres à virgule flottante à précision simple

Les nombres à virgule flottante à précision simple sont le plus souvent appelés ‘float’ ou ‘real’. Ils sont longs de 4 bytes, et sont formés de la manière suivante, de gauche à droite:
  • Signe: 1 bit
  • Exposant: 8 bits
  • Mantisse: 23 bits
X XXXX XXXX XXX XXXX XXXX XXXX XXXX XXXXX
Signe
1 bit
Exposant
8 bits
Mantisse
23 bits
Le signe indique si le nombre est positif ou négatif (zéro pour les nombres positifs et un pour les négatifs).
L’exposant réel est calculé en soustrayant 127 à la valeur du champ exposant. Il s’agit de l’exposant du nombre, tel qu’exprimé en notation scientifique.
La mantisse complète, parfois aussi appelée significande, doit être considérée comme une valeur sur 24 bits. En effet, comme nous utilisons la notation scientifique, il y a un premier bit implicite (appelé parfois le bit caché) dont la valeur est fixée à 1, étant donné qu’il n’y a jamais de 0 en tout premier, en notation scientifique.
Par exemple, on ne dira jamais 0.123 · 105 mais bien 1.23 · 104.
La conversion est effectuée de la manière suivante:
-1S · 1.M · 2( E - 127 )
Où S est le signe, M la mantisse, et E l’exposant.

2. Exemple

Prenons par exemple 0100 0000 1011 1000 0000 0000 0000 0000, ou 0x40B80000 en notation hexadécimale.
Hexadécimal 4 0 B 8 0 0 0 0
Binaire 0100 0000 1011 1000 0000 0000 0000 0000
Signe Exposant Mantisse
0 1000 0001 (1) 011 1000 0000 0000 0000 0000
  • Le signe est 0, le nombre est donc positif.
  • Le champ exposant est 1000 0001, autrement dit 129 en décimal. La valeur réelle de l’exposant est donc 129 – 127, ce qui nous donne 2.
  • La mantisse, avec le premier bit implicite, est 1011 1000 0000 0000 0000 0000.
La représentation finale du nombre en notation scientifique binaire est donc:
-10 · 1.0111 · 22
Mathématiquement, cela veut dire:
1 · ( 1 · 20 + 0 · 2-1 + 1 · 2-2 + 1 · 2-3 + 1 · 2-4 ) · 22
( 20 + 2-2 + 2-3 + 2-4 ) · 22
22 + 20 + 2-1 + 2-2
4 + 1 + 0.5 + 0.25
La valeur du nombre à virgule flottante est donc 5.75.

3. Nombres spéciaux

En fonction de la valeur du champ exposant, certain nombres peuvent avoir une valeur spéciale. Ils peuvent être:
  • Des nombres dénormalisés
  • Zéro
  • Infini
  • NaN (not a number, pas un nombre)

3.1. Nombres dénormalisés

Si la valeur du champ exposant est 0, et que la valeur de la mantisse est plus grande que 0, le nombre doit alors être traité comme un nombre dénormalisé.
Dans un tel cas, l’exposant n’est pas -127, mais -126. Le premier bit implicite, quand à lui, n’est pas 1 mais 0.
Cela permet la représentation de nombres plus petits.
La notation scientifique d’un nombre dénormalisé est:
-1S · 0.M · 2-126

3.2. Zéro

Si le champ exposant et la mantisse ont tous deux pour valeur 0, le nombre final est 0. Le bit du signe est autorisé, permettant un zéro positif ou négatif, même si cela n’a pas un grand sens mathématiquement.
Il est à noter que zéro peut être considéré comme un nombre dénormalisé. Dans ce cas, cela veut dire 0 · 2-126, ce que nous donne bien zéro.

3.3. Infini

Si la valeur du champ exposant est 255 (les 8 bits définis à 1) et si la valeur de la mantisse est 0, le nombre correspont à l’infinité, soit posistive ou négative, en fonction du signe.

3.4. NaN

Si la valeur du champ exposant est 255 (les 8 bits définis à 1) et si la valeur de la mantisse n’est pas 0, la valeur ne doit pas être considérée comme un nombre. Dans un tel cas, le signe n’a aucune signification.

4. Portée

La portée est différente pour les nombres normalisés et dénormalisés. La portée pour ces deux cas est indiquée ci-dessous:

4.1 Nombres normalisés

  • Minimum: ±1.1754944909521E-38 / ±1.00000000000000000000001-126
  • Maximum: ±3.4028234663853E+38 / ±1.11111111111111111111111128

4.2 Nombres dénormalisés

  • Minimum: ±1.4012984643248E-45 / ±0.00000000000000000000001-126
  • Maximum: ±1.1754942106924E-38 / ±0.11111111111111111111111-126

5. Exemple de code C

Voici un exemple de programme C qui converti un nombre binaire en sa représentation en nombre à virgule flottante:
/* System includes */
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

/* Definition of the boolean data type */
typedef enum { FALSE, TRUE } boolean;

/**
* Converts a integer to its float representation
* 
* This function converts a 32 bits integer to a single precision floating point
* number, as specified by the IEEE Standard for Floating-Point Arithmetic
* (IEEE 754). This standard can be found at the folowing address:
* {@link http://ieeexplore.ieee.org/servlet/opac?punumber=4610933}
* 
* @param unsigned long The integer to convert to a floating point value
* @return float The floating point number
* @author Jean-David Gadina <macmade@netinfluence.com>
*/
float binaryToFloat( unsigned long binary )
{
    /* Gets the sign field */
    /* Bit 0, left to right */
    boolean sign = binary >> 31;

    /* Gets the exponent field */
    /* Bits 1 to 8, left to right */
    unsigned char exp = ( ( binary >> 23 ) & 0xFF );

    /* Gets the mantissa field */
    /* Bits 9 to 32, left to right */
    unsigned long mantissa = ( binary & 0x7FFFFF );

    /* Storage for the return value */
    float floatValue = 0;

    /* Counter */
    signed int i = 0;

    /* Checks the values of the exponent and the mantissa fields to handle special numbers */
    if( exp == 0 && mantissa == 0 ) {

        /* Zero - No need for a computation even if it can be considered as a denormalized number */
       return 0;

    } else if( exp == 255 && mantissa == 0 ) {

        /* Infinity */
        return 0;

    } else if( exp == 255 && mantissa != 0 ) {

        /* Not a number */
        return 0;

    } else if( exp == 0 && mantissa != 0 ) {

        /* Denormalized number - Exponent is fixed to -126 */
        exp = -126;

    } else {

        /* Computes the real exponent */
        exp = exp - 127;

        /* Adds the implicit bit to the mantissa */
        mantissa = mantissa | 0x800000;
    }

    /* Process the 24 bits of the mantissa */
    for( i = 0; i > -24; i-- ) {

        /* Checks if the current bit is set */
        if( mantissa & ( 1 << ( i + 23 ) ) ) {

            /* Adds the value for the current bit */
            /* This is done by computing two raised to the power of the exponent plus the bit position */
            /* (negative if it's after the implicit bit, as we are using scientific notation) */
            floatValue += ( float )pow( 2, i + exp );
        }
    }

    /* Returns the final float value */
    return ( sign == FALSE ) ? floatValue : -floatValue;
}

/**
* C main() function
* 
* @return int The exit status
*/
int main( void )
{
    printf( "%f\n", binaryToFloat( 0x40B80000 ) );
    return EXIT_SUCCESS;
}

You may also like

1 comment

Gilles Doge 24 septembre 2009 at 13 h 06 min

Aaah les floating point et la norme IEEE 754, que des souvenirs!
D’ailleurs il me semble qu’elle a été révisée l’année passée (IEEE 754-2008: http://ieeexplore.ieee.org/xpl/free…). Je suis pas mathématicien, mais de ce que j’ai pu lire, la révision d’août 2008 concerne principalement les arrondis des fonctions « élémentaires » (sin, cos, exp, log, …).

Bon résumé!
Et si jamais pour les doubles stocké sur 64 bits: S=1bit, E=11bits, M=52bits

Reply

Leave a Comment