Результаты исследования NormalizeDouble

 

Здравствуйте!

В продолжение моей темы по поиску аналога NormalizeDouble для C++.

Немного исследовал MQL-функцию NormalizeDouble (ранее бывшую для меня эталоном правильности округления).

Вот массив значений, округление которых до 4-го знака с помощью NormalizeDouble (например, при расчете уровня котировки) даст неверный результат.

Исследуемый диапазон - от 0.00001 до 1.0


double AR[] = { 0.00015, 0.00145, 0.00465, 0.00565, 0.00815, 0.00845, 0.01045, 0.01075, 0.01245,
0.01275, 0.01605, 0.01775, 0.01945, 0.02005, 0.02175, 0.02235, 0.02405, 0.02465, 0.03235,
0.03405, 0.03525, 0.03575, 0.03695, 0.03865, 0.03985, 0.04035, 0.04155, 0.04205, 0.04325,
0.04495, 0.04615, 0.04665, 0.04785, 0.04835, 0.04955, 0.06445, 0.06495, 0.06545, 0.06735,
0.06785, 0.06835, 0.07075, 0.07125, 0.07365, 0.07415, 0.07465, 0.07705, 0.07755, 0.07995,
0.08045, 0.08095, 0.08285, 0.08335, 0.08385, 0.08625, 0.08675, 0.08725, 0.08915, 0.08965,
0.09015, 0.09255, 0.09305, 0.09355, 0.09545, 0.09595, 0.09645, 0.09885, 0.09935, 0.10175,
0.10225, 0.12815, 0.12865, 0.12915, 0.12965, 0.13015, 0.13065, 0.13445, 0.13495, 0.13545,
0.13595, 0.13645, 0.13695, 0.14075, 0.14125, 0.14175, 0.14225, 0.14275, 0.14325, 0.14705,
0.14755, 0.14805, 0.14855, 0.14905, 0.14955, 0.15335, 0.15385, 0.15435, 0.15485, 0.15535,
0.15585, 0.15965, 0.16015, 0.16065, 0.16115, 0.16165, 0.16215, 0.16595, 0.16645, 0.16695,
0.16745, 0.16795, 0.17225, 0.17275, 0.17325, 0.17375, 0.17425, 0.17855, 0.17905, 0.17955,
0.18005, 0.18055, 0.18485, 0.18535, 0.18585, 0.18635, 0.18685, 0.19065, 0.19115, 0.19165,
0.19215, 0.19265, 0.19315, 0.19695, 0.19745, 0.19795, 0.19845, 0.19895, 0.19945, 0.20325,
0.20375, 0.20425, 0.20475, 0.25025, 0.25075, 0.25125, 0.25175, 0.25225, 0.25275, 0.25325,
0.25375, 0.25425, 0.25475, 0.25525, 0.26285, 0.26335, 0.26385, 0.26435, 0.26485, 0.26535,
0.26585, 0.26635, 0.26685, 0.26735, 0.26785, 0.27545, 0.27595, 0.27645, 0.27695, 0.27745,
0.27795, 0.27845, 0.27895, 0.27945, 0.27995, 0.28045, 0.28755, 0.28805, 0.28855, 0.28905,
0.28955, 0.29005, 0.29055, 0.29105, 0.29155, 0.29205, 0.29255, 0.29305, 0.30015, 0.30065,
0.30115, 0.30165, 0.30215, 0.30265, 0.30315, 0.30365, 0.30415, 0.30465, 0.30515, 0.31275,
0.31325, 0.31375, 0.31425, 0.31475, 0.31525, 0.31575, 0.31625, 0.31675, 0.31725, 0.31775,
0.32535, 0.32585, 0.32635, 0.32685, 0.32735, 0.32785, 0.32835, 0.32885, 0.32935, 0.32985,
0.33035, 0.33795, 0.33845, 0.33895, 0.33945, 0.33995, 0.34045, 0.34095, 0.34145, 0.34195,
0.34245, 0.34295, 0.35005, 0.35055, 0.35105, 0.35155, 0.35205, 0.35255, 0.35305, 0.35355,
0.35405, 0.35455, 0.35505, 0.35555, 0.36265, 0.36315, 0.36365, 0.36415, 0.36465, 0.36515,
0.36565, 0.36615, 0.36665, 0.36715, 0.36765, 0.37525, 0.37575, 0.37625, 0.37675, 0.37725,
0.37775, 0.37825, 0.37875, 0.37925, 0.37975, 0.38025, 0.38785, 0.38835, 0.38885, 0.38935,
0.38985, 0.39035, 0.39085, 0.39135, 0.39185, 0.39235, 0.39285, 0.40045, 0.40095, 0.40145,
0.40195, 0.40245, 0.40295, 0.40345, 0.40395, 0.40445, 0.40495, 0.40545, 0.50045, 0.50145,
0.50245, 0.50345, 0.50445, 0.50655, 0.50755, 0.50855, 0.50955, 0.51055, 0.51155, 0.51255,
0.51355, 0.51455, 0.51555, 0.51655, 0.51965, 0.52065, 0.52165, 0.52265, 0.52365, 0.52465,
0.52565, 0.52665, 0.52765, 0.52865, 0.52965, 0.53175, 0.53275, 0.53375, 0.53475, 0.53575,
0.53675, 0.53775, 0.53875, 0.53975, 0.54075, 0.54175, 0.54385, 0.54485, 0.54585, 0.54685,
0.54785, 0.54885, 0.54985, 0.55085, 0.55185, 0.55285, 0.55385, 0.55485, 0.55695, 0.55795,
0.55895, 0.55995, 0.56095, 0.56195, 0.56295, 0.56395, 0.56495, 0.56595, 0.56695, 0.56905,
0.57005, 0.57105, 0.57205, 0.57305, 0.57405, 0.57505, 0.57605, 0.57705, 0.57805, 0.57905,
0.58215, 0.58315, 0.58415, 0.58515, 0.58615, 0.58715, 0.58815, 0.58915, 0.59015, 0.59115,
0.59215, 0.59425, 0.59525, 0.59625, 0.59725, 0.59825, 0.59925, 0.60025, 0.60125, 0.60225,
0.60325, 0.60425, 0.60635, 0.60735, 0.60835, 0.60935, 0.61035, 0.61135, 0.61235, 0.61335,
0.61435, 0.61535, 0.61635, 0.61735, 0.61945, 0.62045, 0.62145, 0.62245, 0.62345, 0.62445,
0.62545, 0.62645, 0.62745, 0.62845, 0.62945, 0.63155, 0.63255, 0.63355, 0.63455, 0.63555,
0.63655, 0.63755, 0.63855, 0.63955, 0.64055, 0.64155, 0.64465, 0.64565, 0.64665, 0.64765,
0.64865, 0.64965, 0.65065, 0.65165, 0.65265, 0.65365, 0.65465, 0.65675, 0.65775, 0.65875,
0.65975, 0.66075, 0.66175, 0.66275, 0.66375, 0.66475, 0.66575, 0.66675, 0.66885, 0.66985,
0.67085, 0.67185, 0.67285, 0.67385, 0.67485, 0.67585, 0.67685, 0.67785, 0.67885, 0.67985,
0.68195, 0.68295, 0.68395, 0.68495, 0.68595, 0.68695, 0.68795, 0.68895, 0.68995, 0.69095,
0.69195, 0.69405, 0.69505, 0.69605, 0.69705, 0.69805, 0.69905, 0.70005, 0.70105, 0.70205,
0.70305, 0.70405, 0.70715, 0.70815, 0.70915, 0.71015, 0.71115, 0.71215, 0.71315, 0.71415,
0.71515, 0.71615, 0.71715, 0.71925, 0.72025, 0.72125, 0.72225, 0.72325, 0.72425, 0.72525,
0.72625, 0.72725, 0.72825, 0.72925, 0.73135, 0.73235, 0.73335, 0.73435, 0.73535, 0.73635,
0.73735, 0.73835, 0.73935, 0.74035, 0.74135, 0.74235, 0.74445, 0.74545, 0.74645, 0.74745,
0.74845, 0.74945, 0.75045, 0.75145, 0.75245, 0.75345, 0.75445, 0.75655, 0.75755, 0.75855,
0.75955, 0.76055, 0.76155, 0.76255, 0.76355, 0.76455, 0.76555, 0.76655, 0.76965, 0.77065,
0.77165, 0.77265, 0.77365, 0.77465, 0.77565, 0.77665, 0.77765, 0.77865, 0.77965, 0.78175,
0.78275, 0.78375, 0.78475, 0.78575, 0.78675, 0.78775, 0.78875, 0.78975, 0.79075, 0.79175,
0.79385, 0.79485, 0.79585, 0.79685, 0.79785, 0.79885, 0.79985, 0.80085, 0.80185, 0.80285,
0.80385, 0.80485, 0.80695, 0.80795, 0.80895, 0.80995, 0.81095, 0.81195, 0.81295, 0.81395,
0.81495, 0.81595, 0.81695, 0.81905 };

 

Дело в том, что реально значение 0.00015 в double представляет собой ближайшее к нему значение 0.00014999999999999999

В результате округление 0.00014999999999999999 до четвертого знака дает 0.0001, а не ожидаемое 0.0002


Можете проверить в любом другом языке программирования, особенно под отладчиком:

 

Это правда.

Вчера я просил на форуме подсказать функцию для С++ (достаточно быструю), аналогичную NormalizeDouble, поскольку некоторые результаты округления с помощью SimpleRoundTo до 4-го знака (Borland C++ Builder 6.0, Math.hpp) не совпадали с NormalizeDouble в MQL.

Кстати, сейчас я прогнал SimpleRoundTo в диапазоне 0.00001 - 1.0: 8236 неверных результатов.

У NormalizeDouble в MQL - всего 573 на том же диапазоне значений.

Ваше мнение - как лучше округлять в MQL и в C++ ?

 

Мы округляем вот так:

const double ExtDecimalArray[9] ={ 1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0,  10000000.0, 100000000.0 };
...
 
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double NormalizeDouble(const double val, int digits)
  {
//---
   if(digits<0) digits=0;
   if(digits>8) digits=8;
//---
   double rem,p=ExtDecimalArray[digits];
   double integer=double(__int64(val));
   double rem2=val-integer;
//---
   if(val>0) rem=double(__int64(rem2*p+0.5))/p;
   else      rem=double(__int64(rem2*p-0.5))/p;
//---
   return(integer+rem);
  }
 
Renat:

Мы округляем вот так:

const double ExtDecimalArray[9] ={ 1.0, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0,  10000000.0, 100000000.0 };
...
 
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double NormalizeDouble(const double val, int digits)
  {
//---
   if(digits<0) digits=0;
   if(digits>8) digits=8;
//---
   double rem,p=ExtDecimalArray[digits];
   double integer=double(__int64(val));
   double rem2=val-integer;
//---
   if(val>0) rem=double(__int64(rem2*p+0.5))/p;
   else      rem=double(__int64(rem2*p-0.5))/p;
//---
   return(integer+rem);
  }

Idealno sootvestvujet mojemu variantu, krome gluschovo originalnogo type castinga vmesto floor i cachingom znachenija pow() - eto xorosasja ideja, umensajet overhead

 
AlexanderD:

Кстати, сейчас я прогнал SimpleRoundTo в диапазоне 0.00001 - 1.0: 8236 неверных результатов.

У NormalizeDouble в MQL - всего 573 на том же диапазоне значений.

Ваше мнение - как лучше округлять в MQL и в C++ ?

AlexanderD, вы подобрали для себя метод округления ?


Успехов.

 

__int64

Как с ним работать в MQL ?

Причина обращения: