сравнение на равенство вещественных чисел, в последний раз, надеюсь

 

Наверное, многие по этим граблям прошлись, и даже в хелпе об этом написано, https://www.mql5.com/ru/docs/basis/types/double но всё равно, задача корректного решения этой проблемы так и осталась нерешённой.

Первый пример в хелпе, с функцией EqualDoubles, как мне кажется, хорошо бы вообще убрать. Ну или описать, почему он часто будет некорректен. За тонкостями можно обратиться в http://www.rsdn.ru/article/alg/float.xml#EZNAC или лучше, в http://ltwood.wikidot.com/float

Второй пример, с нормализацией - по сути своей то же, грешит недостатками. Начать нужно с того, что хорошо бы описать алгоритм этой самой нормализации. То что что написано в хелпе, про нормализацию, что это якобы округление - ни о чём не говорит, потому что правил округления, вообще говоря, несколько. У меня в последнее время возникли подозрения, что не всё там гладко у вас.


Ну и, как пишут умные люди, более правильно сравнивать два вещественных числа, в данном случае double, на равенство, по формуле из http://ltwood.wikidot.com/float

fabs(x - y) < (eps + 2.0 * FLT_EPSILON) * (1.0 + fmax(fabs(x), fabs(y)))

ЗЫ вообще, есть ещё более корректный алгоритм сравнения вещественных чисел, хотя и он не является идеальным,
 с точки зрения охвата всего диапазона проблем. Беда с этим алгоритмом в том, что он ужасно медленный.  
Документация по MQL5: Основы языка / Типы данных / Вещественные типы (double, float)
Документация по MQL5: Основы языка / Типы данных / Вещественные типы (double, float)
  • www.mql5.com
Основы языка / Типы данных / Вещественные типы (double, float) - Документация по MQL5
 

Вариант сравнения.

 double a,b:
  . . .
 if(fabs(a-b)<=16*DBL_EPSILON*fmax(fabs(a),fabs(b)))
   {
   . . . Числа равны с относительной точностью 16*DBL_EPSILON
   }

Игнорируется разница в примерно 4 младших бита  мантиссы. Или примерно полторы младшие значащие десятичные цифры из примерно 16-ти имеющихся. 

 

Коррекция для малых, близких к нулю чисел:

if(fabs(a-b)<=DBL_MIN+16*DBL_EPSILON*fmax(fabs(a),fabs(b)))  ;  // if(a==b)

Наверное, можно несколько повысить точность сравнения(?). Окончательно получим:

if(fabs(a-b)<=DBL_MIN+8*DBL_EPSILON*fmax(fabs(a),fabs(b)))  ;  // if(a==b)

 

 

Хочу тоже высказаться.

Во многих трецдерских вопросах (пробой уровня, ...), сравнивать даблы надо с минимальным шагом цены на графике. ИМХО для этих целей не указано правильного решения как здесь, так и в справке. Я думаю что правильным решением будет округление одного из чисел к цене, далее сравнение разности с ценовым шагом. Я такую схему реализовал так:

bool ComparePrices(double highestPrice, double lowestPrice, bool more, int digits)
{       //Если highestPrice < lowestPrice return(true); если highestPrice == lowestPrice && more == true return(false)
   static int shift = -1;   // сдвигает все значимые разряды в целую часть
   if(shift == -1)       
   {
      shift = 1;
      int count = digits;
      while(count > 0)
      {
         shift *= 10;
         count --;
      }
   }       

   highestPrice *= shift;
   lowestPrice *= shift;

   highestPrice = MathRound(highestPrice);       
                
   double rezult = highestPrice - lowestPrice;
   if(rezult > 0.5)
      return(false);
   if(rezult <= -0.5)
      return(true);
   if(more)
      return(false);
   return(true);
}
Если инструменты для сравнения разные (их несколько), тогда необходимо переделать кусок, в котором рассчитывается  shift (так сделано для большей производительности). 

 

 

 

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