Вещественные типы (double, float)

Вещественные типы (или типы с плавающей точкой) представляют значения, имеющие дробную часть. В языке MQL5 есть два типа для чисел с плавающей точкой. Способ представления вещественных чисел в машинной памяти определен стандартом IEEE 754 и не зависит от платформ, операционных систем и языков программирования.

Тип

Размер в байтах

Минимальное положительное значение

Максимальное значение

Аналог в С++

float

4

1.175494351e-38

3.402823466e+38

float

double

8

2.2250738585072014e-308

1.7976931348623158e+308

double

 

double

Вещественный тип чисел double занимает 64 бита (1 знаковый бит, 11 бит экспоненты и 52 бита мантиссы).

float

Вещественный тип чисел float занимает 32 бита (1 знаковый бит, 8 бит экспоненты и 23 бита мантиссы).

vector

Одномерный массив чисел типа double. Память под данные распределяется динамически.  Свойства вектора могут быть получены при помощи методов, при этом размер вектора может быть изменен. В шаблонных функциях можно использовать запись vector<double>.

vectorf

Одномерный массив чисел типа float, может использоваться вместо vector, если потеря точности не имеет значения. В шаблонных функциях можно использовать запись vector<float>.

vectorc

Одномерный массив чисел типа complex, предназначен для работы с комплесными числами.  В шаблонных функциях можно использовать запись vector<complex>.  На данный момент операции над векторами типа vectorc еще не реализованы.

matrix

Матрица — двумерный массив чисел типа double. Память под элементы матрицы распределяется динамически. Свойства матрицы могут быть получены при помощи методов, при этом размер вектора может быть изменен. В шаблонных функциях можно использовать запись matrix<double>.

matrixf

Двумерный массив чисел типа float, может использоваться вместо matrix, если потеря точности не имеет значения. В шаблонных функциях можно использовать запись matrix<float>.

matrixc

Двумерный массив чисел типа complex, предназначен для работы с комплесными числами.  В шаблонных функциях можно использовать запись matrix<complex>.  На данный момент операции над матрицами типа matrixc еще не реализованы.

 

Имя double означает, что точность этих чисел вдвое превышает точность чисел типа float. В большинстве случаев тип double является наиболее удобным. Ограниченной точности чисел float во многих случаях попросту недостаточно. Причина, по которой тип float все еще используется, - экономия памяти при хранении (это важно для больших массивов вещественных чисел).

Константы с плавающей точкой состоят из целой части, точки (.) и дробной части. Целая и дробная части представляют собой последовательности десятичных цифр.

Примеры:

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

Существует научный способ записи вещественных констант, зачастую этот способ записи более компактный, чем традиционный.

Пример:

   double c1=1.12123515e-25;
   double c2=0.000000000000000000000000112123515; // 24 нуля после десятичной точки
   
   Print("1. c1 = ",DoubleToString(c1,16));
   // Результат: 1. c1 = 0.0000000000000000
   
   Print("2. c1 = ",DoubleToString(c1,-16));
   // Результат: 2. c1 = 1.1212351499999999e-025
 
   Print("3. c2 = ",DoubleToString(c2,-16));
   // Результат: 3. c2 = 1.1212351499999999e-025

Необходимо помнить, что вещественные числа хранятся в памяти компьютера с некоторой ограниченной точностью в двоичной системе счисления, в то время как общепринятой в использовании является десятичная система счисления. Поэтому многие числа, которые точно записываются в десятичной системе, в двоичной системе можно записать только в виде бесконечной дроби.

Например, числа 0.3 и 0.7 представлены в компьютере бесконечными дробями, в то время как число 0.25 хранится точно, так как представляет из себя степень двойки.

В связи с этим, категорически не рекомендуется сравнивать между собой два вещественных числа на равенство, так как такое сравнение не является корректным.

Пример:

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");
// Результат: 1/3 + 4/3 != 5/3
  }

Если все же необходимо сравнить на равенство два вещественных числа, то можно сделать это двумя разными способами. Первый способ заключается в сравнении разницы между двумя числами с какой-то малой величиной, задающей точность сравнения.

Пример:

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,"equals",f_val);
   else 
      Print("Different: d_val = ",DoubleToString(d_val,16),"  f_val = ",DoubleToString(f_val,16));
// Результат: Different: d_val= 0.7000000000000000   f_val= 0.6999999880790710
  }

Необходимо отметить, что значение параметра epsilon в приведенном примере не может быть меньше предопределенной константы DBL_EPSILON. Значение этой константы  2.2204460492503131e-016. Для типа float соответствующая константа FLT_EPSILON = 1.192092896e-07. Смысл этих значений таков, что это наименьшее значение, удовлетворяющее условию 1.0+DBL_EPSILON != 1.0 (для чисел типа float 1.0+FLT_EPSILON != 1.0).

Второй способ предполагает сравнивать нормализованную разность двух вещественных чисел с нулевым значением. Сравнивать разность нормализованных чисел с нулём бесполезно, так как в результате любой математической операции с нормализованными числами результат получается ненормализованным.

Пример:

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,"equals",f_val);
   else 
      Print("Different: d_val = ",DoubleToString(d_val,16),"  f_val = ",DoubleToString(f_val,16));
// Результат: Different: d_val= 0.3000000000000000   f_val= 0.3000000119209290
  }

В результате некоторых операций математического сопроцессора может получиться недействительное вещественное число, которое нельзя использовать в математических операциях и операциях сравнения, так как результат выполнения операций над недействительными вещественными числами неопределен. Например, при попытке вычислить арксинус от 2, результатом будет минус бесконечность.

Пример:

   double abnormal = MathArcsin(2.0);
   Print("MathArcsin(2.0) =",abnormal);
// Результат:  MathArcsin(2.0) = -1.#IND

Кроме минус бесконечности существуют плюс бесконечность и NaN (не число). Чтобы определить, что данное число недействительно, можно использовать функцию MathIsValidNumber(). По стандарту IEEE они имеют специальное машинное представление. Например, плюс бесконечность для типа double имеет битовое представление 0x7FF0 0000 0000 0000.

Примеры:

struct str1
  {
   double d;
  };
struct str2
  {
   long l;
  };
 
//--- начнем 
   str1 s1;
   str2 s2;
//---
   s1.d=MathArcsin(2.0);        // получим недействительное число -1.#IND
   s2=s1;
   printf("1.  %f %I64X",s1.d,s2.l);
//---
   s2.l=0xFFFF000000000000;     // недействительное число -1.#QNAN
   s1=s2;
   printf("2.  %f %I64X",s1.d,s2.l);
//---
   s2.l=0x7FF7000000000000;     // наиобльшее нечисло SNaN
   s1=s2;
   printf("3.   %f %I64X",s1.d,s2.l);
//---
   s2.l=0x7FF8000000000000;     // наименьшее нечисло QNaN
   s1=s2;
   printf("4.   %f %I64X",s1.d,s2.l);
//---
   s2.l=0x7FFF000000000000;     // наибольшее нечисло QNaN
   s1=s2;
   printf("5.   %f %I64X",s1.d,s2.l);
//---
   s2.l=0x7FF0000000000000;     // плюс бесконечность 1.#INF и наименьшее нечисло SNaN
   s1=s2;
   printf("6.   %f %I64X",s1.d,s2.l);
//---
   s2.l=0xFFF0000000000000;     // минус бесконечность -1.#INF
   s1=s2;
   printf("7.  %f %I64X",s1.d,s2.l);
//---
   s2.l=0x8000000000000000;     // отрицательный ноль -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;     // наибольшее нормализованное число (MAX_DBL)
   s1=s2;
   printf("11.  %.16e %I64X",s1.d,s2.l);
//---
   s2.l=0x0010000000000000;     // наименьшее положительное нормализованное (MIN_DBL)
   s1=s2;
   printf("12.  %.16e %.16I64X",s1.d,s2.l);
//---
   s1.d=0.7;                    // покажем, что число 0.7 – бесконечная дробь
   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 
*/

Смотри также

DoubleToString, NormalizeDouble, Константы числовых типов