Types Réels (double, float)

Les types réels (ou types à virgule flottante) représentent les valeurs ayant une partie fractionnelle. Dans le langage MQL5, il existe deux types de chiffres à virgule flottante. La méthode de représentation des nombres réels dans la mémoire de l'ordinateur est définie par le standard IEEE 754 et est dépendant de la plateforme, du système d'exploitation et du langage de programmation.

Type

Taille en octets

Valeur Positive Minimale

Valeur Maximum

Analogie en C++

float

4

1.175494351e-38

3.402823466e+38

float

double

8

2.2250738585072014e-308

1.7976931348623158e+308

double

Le nom double signifie que la précision de ces nombres est le double de la précision des nombres de type float. Dans la plupart des cas, le type double est le plus pratique. Dans de nombreux cas, la précision limitée des nombres float n'est pas suffisante. La raison de l'utilisation du type float est l'économie de mémoire (c'est important pour les gros tableaux de nombres réels).

Les constantes à virgule flottante consistent en une partie entière, un point (.) et une partie fractionnelle. Les parties entière et fractionnelle sont des suites de chiffres décimaux.

Exemples :

   double a=12.111;
   double b=-956.1007;
   float  c =0.0001;
   float  d =16;

Il existe une façon scientifique d'écrire des constantes réelles, souvent cette méthode est plus compacte.

Exemple :

   double c1=1.12123515e-25;
   double c2=0.000000000000000000000000112123515// 24 zéros après la virgule
   
   Print("1. c1 =",DoubleToString(c1,16));
   // Résultat : 1. c1 = 0.0000000000000000
   
   Print("2. c1 =",DoubleToString(c1,-16));
   // Résultat : 2. c1 = 1.1212351499999999e-025
 
   Print("3. c2 =",DoubleToString(c2,-16));
   // Résultat : 3. c2 = 1.1212351499999999e-025

Il faut se souvenir que les nombres réels sont stockés en mémoire avec une précision limitée dans le système binaire, alors que généralement la notation décimale est utilisée. C'est pourquoi de nombreux nombres qui sont représentés précisemment dans le système décimal ne peuvent être écrit qu'avec une fraction infinie dans le système binaire.

Par exemple, les nombres 0.3 et 0.7 sont représentés dans l'ordinateur comme des fractions infinies, tandhis que 0.25 est stocké de façon exacte car il représente la puissance de 2.

A cet égard, il est fortement recommandé de ne pas comparer deux nombres réels pour l'égalité, car les comparer n'est pas la bonne façon.

Exemple :

void OnStart()
  {
//---
   double three=3.0;
   double x,y,z;
   x=1/three;
   y=4/three;
   z=5/three;
   if(x+y==z) Print("1/3 + 4/3 == 5/3");
   else Print("1/3 + 4/3 != 5/3");
// Résultat : 1/3 + 4/3 != 5/3
  }

Si vous devez comparer l'égalité de deux nombres réels, vous pouvez le faire de deux façons différentes. La première est de comparer la différence entre deux nombres avec une petite valeur spécifiant la précision de la comparaison.

Exemple :

bool EqualDoubles(double d1,double d2,double epsilon)
  {
   if(epsilon<0) epsilon=-epsilon;
//---
   if(d1-d2>epsilon) return false;
   if(d1-d2<-epsilon) return false;
//---
   return true;
  }
void OnStart()
  {
   double d_val=0.7;
   float  f_val=0.7;
   if(EqualDoubles(d_val,f_val,0.000000000000001)) Print(d_val," est égal à ",f_val);
   else Print("Différents : d_val = ",DoubleToString(d_val,16),
              "  f_val = ",DoubleToString(f_val,16));
// Résultat : Différents : d_val= 0.7000000000000000   f_val= 0.6999999880790710
  }

Notez que la valeur d'epsilon dans l'exemple ci-dessus ne peut pas être inférieur à la constante prédéfinie DBL_EPSILON. La valeur de cette constante est 2.2204460492503131e-016. La constante correspondant au type float est FLT_EPSILON = 1.192092896e-07. La signification de ces valeurs est la suivante : c'est la plus petite valeur qui satisfait la condition  1.0 + DBL_EPSILON ! = 1.0 (pour les nombres de type float 1.0 + FLT_EPSILON ! = 1.0).

La deuxième façon utilise la comparaison de la différence normalisée de deux nombres réels avec zéro. Il est inutile de comparer la différence des nombres normalisés avec zéro, car toute opération mathématique avec des nombres normalisés donne un résultat non normalisé.

Exemple :

bool CompareDoubles(double number1,double number2)
  {
   if(NormalizeDouble(number1-number2,8)==0) return(true);
   else return(false);
  }
void OnStart()
  {
   double d_val=0.3;
   float  f_val=0.3;
   if(CompareDoubles(d_val,f_val)) Print(d_val," égal ",f_val);
   else Print("Différents : d_val = ",DoubleToString(d_val,16),
              "  f_val = ",DoubleToString(f_val,16));
// Résultat : Différents : d_val= 0.3000000000000000   f_val= 0.3000000119209290
  }

Certaines opérations du coprocesseur mathématique peuvent générer des nombres r&els invalides, qui ne peuvent pas être utilisés dans les opérations mathématiques et dans les comparaisons, car le résultat d'opération avec des nombres réels est indéfini. Par exemple, lors du calcul de l'arc-sinus de 2, le résultat est l'infini négatif.

Exemple :

   double abnormal = MathArcsin(2.0);
   Print("MathArcsin(2.0) =",abnormal);
// Résultat :  MathArcsin(2.0) = -1.#IND

En plus de l'infini négatif, il y a également l'infini positif et NaN (NotANumber, pas un nombre). Pour savoir si un nombre est invalide, vous pouvez appeler la fonction MathIsValidNumber(). Selon le standard IEEE, ils ont une représentation machine spéciale. Par exemple, l'infini positif pour le type double est représenté par 0x7FF0 0000 0000 0000.

Exemples :

struct str1
  {
   double d;
  };
struct str2
  {
   long l;
  };
 
//--- Début
   str1 s1;
   str2 s2;
//---
   s1.d=MathArcsin(2.0);        // Calcule le nombre invalide -1.#IND
   s2=s1;
   printf("1.  %f %I64X",s1.d,s2.l);
//---
   s2.l=0xFFFF000000000000;     // nombre invalide -1.#QNAN
   s1=s2;
   printf("2.  %f %I64X",s1.d,s2.l);
//---
   s2.l=0x7FF7000000000000;     // plus grand SNaN (NotANumber)
   s1=s2;
   printf("3.   %f %I64X",s1.d,s2.l);
//---
   s2.l=0x7FF8000000000000;     // plus petit QNaN
   s1=s2;
   printf("4.   %f %I64X",s1.d,s2.l);
//---
   s2.l=0x7FFF000000000000;     // plus grand QNaN
   s1=s2;
   printf("5.   %f %I64X",s1.d,s2.l);
//---
   s2.l=0x7FF0000000000000;     // Infini positif 1.#INF et plus petit SNaN
   s1=s2;
   printf("6.   %f %I64X",s1.d,s2.l);
//---
   s2.l=0xFFF0000000000000;     // Infini négatif -1.#INF
   s1=s2;
   printf("7.  %f %I64X",s1.d,s2.l);
//---
   s2.l=0x8000000000000000;     // Zéro négatif -0.0
   s1=s2;
   printf("8.  %f %I64X",s1.d,s2.l);
//---
   s2.l=0x3FE0000000000000;     // 0.5
   s1=s2;
   printf("9.   %f %I64X",s1.d,s2.l);
//---
   s2.l=0x3FF0000000000000;     // 1.0
   s1=s2;
   printf("10.  %f %I64X",s1.d,s2.l);
//---
   s2.l=0x7FEFFFFFFFFFFFFF;     // Plus grand nombre normalisé (MAX_DBL)
   s1=s2;
   printf("11.  %.16e %I64X",s1.d,s2.l);
//---
   s2.l=0x0010000000000000;     // Plus petit nombre positif normalisé (MIN_DBL)
   s1=s2;
   printf("12.  %.16e %.16I64X",s1.d,s2.l);
//---
   s1.d=0.7;                    // Montre que le nombre 0.7 est une fraction infinie
   s2=s1;
   printf("13.  %.16e %.16I64X",s1.d,s2.l);
/*
1.  -1.#IND00 FFF8000000000000
2.  -1.#QNAN0 FFFF000000000000
3.   1.#SNAN0 7FF7000000000000
4.   1.#QNAN0 7FF8000000000000
5.   1.#QNAN0 7FFF000000000000
6.   1.#INF00 7FF0000000000000
7.  -1.#INF00 FFF0000000000000
8.  -0.000000 8000000000000000
9.   0.500000 3FE0000000000000
10.  1.000000 3FF0000000000000
11.  1.7976931348623157e+308 7FEFFFFFFFFFFFFF
12.  2.2250738585072014e-308 0010000000000000
13.  6.9999999999999996e-001 3FE6666666666666 
*/

Voir aussi

DoubleToString, NormalizeDouble, Constantes de Type Numérique