Особенности языка mql5, тонкости и приёмы работы - страница 300

 
Еще нормализовать разность до поинта, и вооще отлично) 
 

Eсли два double числа действительно равны по значению, то сравнение "==" будет  true, и сравнение a == b имеет смысл абсолютно и равно сравнению a - b == 0.0.

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

Другое дело когда хотим сделать сравнение с указанной точностью, а не сравнение значений переменных (например, две котировки), то полезно сравнивать разницу с tickSize, а не с point.

Код, демонстрирующий вышесказанное:

input double      Input1 = 0.1;
input double      Input2 = 0.2;
input double      Input3 = 0.3;

void OnStart()
{
  double a = Input1 + Input2;
  double b = Input3;

  Print ("a = ", a);                              //a = 0.30000000000000004
  Print ("b = ", b);                              //b = 0.3
  Print (a == b);                                 //false
  Print (a - b);                                  //5.551115123125783e-17
  Print (Input1 + Input2 == Input3);              //false
  Print (Input1 + Input2 - Input3 < DBL_EPSILON); //true
  Print (DBL_EPSILON);                            //2.220446049250313e-16
  
  double d1 = DBL_MAX;              
  double d2 = DBL_MAX;              
  Print (d1 == d2);                               //true
  Print (d1 - d2 == 0.0);                         //true
  Print (d1 - d2 < DBL_EPSILON);                  //true
}
 
Andrey Dik point.

Код, демонстрирующий вышесказанное:

Верно, что a == b работает, если оба значения абсолютно одинаковы в памяти, как в ваших примерах с константами.

Но на практике большинство удвоений происходит в результате операций, которые вносят ошибки округления, и именно здесь == (а также a - b == 0.0) становятся ненадежными.

Поэтому рекомендуется использовать MathAbs(a - b) < ε, где ε зависит от контекста ( _Point, размер тика или DBL_EPSILON).

double a = 1.0000001 - 0.0000001;
double b = 1.0;
Print(a == b);                       // false
Print(MathAbs(a - b) < DBL_EPSILON); // true
 
Miguel Angel Vico Alba #:

Верно, что a == b работает, если оба значения абсолютно одинаковы в памяти, как в ваших примерах с константами.

Но на практике большинство удвоений происходит в результате операций, которые вносят ошибки округления, и именно здесь == (а также a - b == 0.0) становятся ненадежными.

Поэтому рекомендуется использовать MathAbs(a - b) < ε, где ε зависит от контекста ( _Point, размер тика или DBL_EPSILON).

Вот, именно из-за выделенного я и написал пост выше. Кем рекомендуется? - для каких случаев рекомендуется?

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

 
Andrey Dik #:

Вот, именно из-за выделенного я и написал пост выше. Кем рекомендовано? - Для каких случаев он рекомендован?

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

Я полностью согласен, Андрей. Дело не в том, что одна форма "хорошая", а другая "плохая", а в том, что нужно понимать, когда использовать каждую из них.

Использование == вполне допустимо, если мы знаем, что значения точны (например, константы или контролируемые данные). Использование MathAbs(a - b) < ε просто добавляет полезный допуск, когда есть предыдущие операции, которые могут внести небольшие погрешности, как это часто бывает в промежуточных вычислениях с double.

В конечном итоге, как вы говорите, все зависит от конкретного случая и уровня точности, который нам нужен.

 

А с чего вдруг вы стали обсуждать сравнение double переменных если изначально был вопрос 

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Особенности языка mql5, тонкости и приёмы работы

Andrei Iakovlev, 2025.05.19 14:56

У какой-то переменной значение равно -1.

Какая проверка будет выполнена быстрее на проверку этого значения: < 0 или == -1?


А дальше предложили

Чем лучше? Как в том анекдоте… «лучше чем …»

 
Putnik #:

Лучше заменить a==b на (a-b)==0, так будет лучше.

Я думаю, что это имеет смысл (и может быть более устойчивым к ошибкам, если a или b - вычисляемое значение) только при сравнении двойных a и b.

Для парных значений

MathAbs(a-b)<DBL_EPSILON

или

NormalizeDouble(a-b, 8)==0

Для целых чисел:

a==b лучше (и понятнее).

 

Для цен использую целые числа. Минимальный шаг в значении цены -- один пункт Point. Это константа, имеет фиксированный размер, т.е. дискретная величена. Но дискретность, это также свойство _целых_ чисел. Следовательно, преобразовав цены к целому числу (int), можно вести вычисления без потери точности.

Например:

котировка EUR/USD = 1.17191, преобразуем в int, получается 117191;

TakeProfit=0.00500, в int получается 500;

ТР=117191+500=117691.

В своей программе использую массивы цен. Спокойно их сравниваю if(a==b), не требуется нормализация NormalizeDouble(). Числа double использую только при обращении к серверу, при получении котировок, при отправлении OrderSend(). 

В функции Print() использую запись вида ".117191" или ".085338". Вполне, понятно.

 
Putnik потери точности.

Например:

Котировка EUR/USD = 1,17191, преобразуем в int, получаем 117191;

TakeProfit=0.00500, в int получаем 500;

ТР=117191+500=117691.

Я использую массивы цен в своей программе. Я легко сравниваю их if(a==b), нормализация NormalizeDouble() не требуется. Я использую двойные числа только при обращении к серверу, при получении котировок и при отправке OrderSend().

В функции Print() я использую запись типа ".117191" или ".085338". Это вполне понятно.

То, что вы делаете, называется арифметикой с фиксированной точкой, и при всех вычислениях необходимо учитывать масштабный коэффициент (например, scale = 100 000 для 5-значных цен).

Однако это не самый эффективный способ на платформе MT5, поскольку изначально цены и лоты принимаются в виде "двойной" плавающей точки.

 

BTW, вот более точный способ сравнить двойные числа a и b (т.е. вычисленные значения, а не константы) на равенство:

MathAbs(a - b) < DBL_EPSILON * MathMax(MathAbs(a), MathAbs(b))

Так как это сравнение может изящно обрабатывать очень большие или очень маленькие числа.

Редактировать:

Для других форм сравнения (>, < и т.д.), пожалуйста, проверьте мою библиотеку здесь: https: //www.mql5.com/en/code/20822