Примеры: Особенности работы с числами типа double в MQL4

 

New article Особенности работы с числами типа double в MQL4 has been published:

В данной заметке собраны советы по решению наиболее часто возникающих ошибок при работе с числами типа double в программах на MQL4.

Author: MetaQuotes Software Corp.

 

> В компьютере точность хранения количества десятичных знаков числа определяется размерами мантиссы и ограничена 52 битами.

Ни как нет. На самом деле процессор поддерживает машинные операции с числами типа extended (собственно говоря это родной тип данных математического сопроцессора).

У него "19 significant digits, exponent -4932 to +4932" - это 80битное число, в отличии от 64битного типа double, о котором Вы пишите.

Ирония ситуации состоит в том, что С, на котором написан МТ, просто напросто не поддерживает этот тип данных.

Соответственно и МТ его не поддерживает - а очень жаль, это наиболее точное число из доступных для нативной обработки процессором.


PS. но в целом, статья нужная, да.

 

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

просто

...в некоторых случаях функция fmod в MSVC6 (и соответственно MathMod), выдает неверный результат.

Йоу! Так держать!

 

Позанудствую.

double MathModCorrect(double a, double b, int precisionRatio = -5)
{ 
   double correction = MathMin(MathAbs(a), MathAbs(b))*MathPow(10, precisionRatio);
   
   if (a > 0)  a += correction;
   else        a -= correction;
   
   int tmpres = a/b;
   return (a - tmpres*b);
}
А вообще штука нужная. Чуть бы поменьше сумбура и побольше четкости в изложении, было бы совсем хорошо.
 
HideYourRichess:

> В компьютере точность хранения количества десятичных знаков числа определяется размерами мантиссы и ограничена 52 битами.

Ни как нет. На самом деле процессор поддерживает машинные операции с числами типа extended (собственно говоря это родной тип данных математического сопроцессора). У него "19 significant digits, exponent -4932 to +4932" - это 80битное число, в отличии от 64битного типа double, о котором Вы пишите.

Спасибо. Скорректировал.

Вы правы, с extended (например, на Delphi) мы бы получили результат -4951 вместо -324 с double.

 
TheXpert:

Позанудствую.

double MathModCorrect(double a, double b, int precisionRatio = -5)
{ 
   double correction = MathMin(MathAbs(a), MathAbs(b))*MathPow(10, precisionRatio);
   
   if (a > 0)  a += correction;
   else        a -= correction;
   
   int tmpres = a/b;
   return (a - tmpres*b);
}
А вообще штука нужная. Чуть бы поменьше сумбура и побольше четкости в изложении, было бы совсем хорошо.

Спасибо. А сумбур получился из-за того, что это заметка типа коллажа различных вопросов.

Насчет добавления погрешности, пусть даже небольшой, то я бы не советовал.

Вот например,

#include <stdlib.mqh>  
double MathModCorrect1(double a, double b)
{  
   int tmpres = a/b;
   return (a - tmpres*b);
}
double MathModCorrect2(double a, double b, int precisionRatio = -5)
{ 
   double correction = MathMin(MathAbs(a), MathAbs(b))*MathPow(10, precisionRatio);
   
   if (a > 0)  a += correction;
   else        a -= correction;
   
   int tmpres = a/b;
   return (a - tmpres*b);
}  
int start()
  { double c1,c2;
 
    c1=MathModCorrect1(5.3,2.0);
    c2=MathModCorrect2(5.3,2.0);
    Alert("MathMod1(5.3,2.0): Output:",c1,", 8 digits precision:"+DoubleToStr(c1,8)+
          ", 15 digits precision:"+ DoubleToStrMorePrecision(c1,15));
    Alert("MathMod2(5.3,2.0): Output:",c2,", 8 digits precision:"+DoubleToStr(c2,8)+
          ", 15 digits precision:"+ DoubleToStrMorePrecision(c2,15));
    c1=MathModCorrect1(18.5,4.2);
    c2=MathModCorrect2(18.5,4.2);
    Alert("MathMod1(18.5,4.2): Output:",c1,", 8 digits precision:"+DoubleToStr(c1,8)+
          ", 15 digits precision:"+ DoubleToStrMorePrecision(c1,15));
    Alert("MathMod2(18.5,4.2): Output:",c2,", 8 digits precision:"+DoubleToStr(c2,8)+
          ", 15 digits precision:"+ DoubleToStrMorePrecision(c2,15));
    c1=MathModCorrect1(14.5,3.5);
    c2=MathModCorrect2(14.5,3.5);
    Alert("MathMod1(14.5,3.5): Output:",c1,", 8 digits precision:"+DoubleToStr(c1,8)+
          ", 15 digits precision:"+ DoubleToStrMorePrecision(c1,15));
    Alert("MathMod2(14.5,3.5): Output:",c2,", 8 digits precision:"+DoubleToStr(c2,8)+
          ", 15 digits precision:"+ DoubleToStrMorePrecision(c2,15));
   // один из случаев с ошибкой в стандартной MathMod
    c1=MathModCorrect1(5.0,0.1);
    c2=MathModCorrect2(5.0,0.1);
    Alert("MathMod1(5.0,0.1): Output:",c1,", 8 digits precision:"+DoubleToStr(c1,8)+
          ", 15 digits precision:"+ DoubleToStrMorePrecision(c1,15));
    Alert("MathMod2(5.0,0.1): Output:",c2,", 8 digits precision:"+DoubleToStr(c2,8)+
          ", 15 digits precision:"+ DoubleToStrMorePrecision(c2,15));
   
   return(0);
  }

Выведет:

MathMod1(5.3,2.0): Output:1.3, 8 digits precision:1.30000000, 15 digits precision:1.300000000000000
MathMod2(5.3,2.0): Output:1.3, 8 digits precision:1.30002000, 15 digits precision:1.300020000000000

MathMod1(18.5,4.2): Output:1.7, 8 digits precision:1.70000000, 15 digits precision:1.699999999999999
MathMod2(18.5,4.2): Output:1.7, 8 digits precision:1.70004200, 15 digits precision:1.700042000000000

MathMod1(14.5,3.5): Output:0.5, 8 digits precision:0.50000000, 15 digits precision:0.500000000000000
MathMod2(14.5,3.5): Output:0.5, 8 digits precision:0.50003500, 15 digits precision:0.500035000000000

MathMod1(5.0,0.1): Output:0, 8 digits precision:0.00000000, 15 digits precision:0.000000000000000
MathMod2(5.0,0.1): Output:0, 8 digits precision:0.00000100, 15 digits precision:0.000001000000000

Так что лично мне больше нравится первый вариант :)
 

Диапазон возможных значений чисел double гораздо шире: -1.7*e-308 до 1.7*e308

Неверная формулировка (раз уж "диапазон" и -1.7*e-308)
 
HideYourRichess:

PS. но в целом, статья нужная, да.


Да, статья жизненная :) Хотя все это давно известно, но вспомнить и увидеть практически было очень интересно.
 
notused:

Диапазон возможных значений чисел double гораздо шире: -1.7*e-308 до 1.7*e308

Неверная формулировка (раз уж "диапазон" и -1.7*e-308)
Спасибо. Скорректировал.
 

Вообще говоря, диапазон вот такой должен быть:

наименование формата single-precision double-precision
длина числа, бит 32 64
смещенная экспонента (E), бит 8 11
остаток от мантиссы (M), бит 23 52
смещение 127 1023
формула расчета денормализованных чисел F =(-1)S∙2(E -126)∙ M/223 F =(-1)S∙2(E -1022)∙M/252
формула расчета нормализованных чисел F =(-1)S∙2(E-127)∙(1+ M/223) F =(-1)S∙2(E-1023)∙(1+M/252)
минимальное число ±2-149≈ ±1,40129846∙e-45 ±2-1074≈ ± 4,94065646∙e-324
максимальное число ±2127∙(2-2-23) ≈ ± 3,40282347∙e+38 ±21023∙(2-2-52) ≈ ± 1,79769313∙e+308


У разработчиков ошибка в описании. Они его наверное взяли из описания С, так же как и все это делают. Но реальность другая.

 
Quantum:
Так что лично мне больше нравится первый вариант :)

Блин. Тоже молодец, поленился протестировать.

double MathModCorrect(double a, double b, int precisionRatio = -5)
{ 
   double a_ = a;
   double correction = MathMin(MathAbs(a), MathAbs(b))*MathPow(10, precisionRatio);
   
   if (a_ > 0) a_ += correction;
   else        a_ -= correction;
   
   int tmpres = a_/b;
   return (a - tmpres*b);
}
Причина обращения: