Новая версия платформы MetaTrader 5 build 2980: Push-уведомления о торговых операциях - страница 32

 
Igor Makanu:

это лишь проверка на допустимые значения для типа 

мой пример тож компилятор прекрасно отследит:

согласен, разница есть. но при оптимизации обе  ситуации будут учтены, IMO.
 
mktr8591:

" просто сравнивает на эквивалентность типов и возможности кастинга к другим типам" - это и значит, что компилятор прекрасно знает тип переменной.

например такой код выдает предупреждение компилятора:

Так что предложение fxsaber  не лишено оснований.

Пример синтетический и полностью оптимизируется компилятором методом распространения констант.

То есть, вместо переменной будет чистая константа в условии. И такое вырожденное условие можно на этапе компиляции отловить.

 
Renat Fatkhullin:

Пример синтетический и полностью оптимизируется компилятором методом распространения констант.

То есть, вместо переменной будет чистая константа в условии. И такое вырожденное условие можно на этапе компиляции отловить.

А (i==i) можно?
 

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

Новая версия платформы MetaTrader 5 build 2980: Push-уведомления о торговых операциях

Igor Makanu, 2021.08.23 21:29

проверил совет из статьи, на Вашем примере

void OnStart()
{
   int min = INT_MIN;
   long i = (min + 0u) / -1;
   Print("i = ", i);
}

так работает при компиляции по F5

К сожалению, так работает совершенно неправильно. добавление +0u приводит min к uint, и -1 тоже приводится к uint, превращаясь в UINT_MAX, поэтому получается выражение 

long i = (uint)min/UINT_MAX;

что равно 0 для любого min типа int. (в вашем примере и печатается "i = 0").


Для себя я пришел к такому выводу. Для операций с int и long, чтобы избежать  UB из-за переполнения:

1. Для операций +,-,*,"/" с двуми переменными int проще всего привести одну из них к long. А потом, если надо, конвертировать  результат в int:

int i1, i2;
//initialize i1,i2
int i3=int((long)i1+i2);
//or
int i3=int((long)i1*i2);
//or 
int i3=int((long)i1/i2);         //check i2!=0 before


2. Для операций  +,-,*   с long  надо привести одну из переменных к ulong (т.к. эти операции выполняются одинаково для ulong и long кроме флагов - переполняния и знака и тп), а потом результат к long.

   long l1, l2, l3;
   //initialize l1,l2,l3
   l3 = long((ulong)l1 + l2);
   l3 = long((ulong)l1 * l2);

3. При  делении long/long переполнение, насколько я знаю, может возникнуть только при LONG_MIN/-1 (и результатом возвращается 0). Поэтому нужна такая конструкция:

   if(l2 != 0)
      l3 = (l1 == LONG_MIN && l2 == -1 ? -LONG_MIN : l1 / l2);

Удобнее, наверно, сделать функцию (пригодная и для int тоже):

//+------------------------------------------------------------------+
//| деление long  / long с защитой от переполнения                   |
//| для нулевого делителя возвращает 0
//+------------------------------------------------------------------+
long DivideLongs(const long a1, const long a2)
  {
   if(a2 == 0)
      return 0;                                      //возврат кода ошибки
   else
      return a1 == LONG_MIN && a2 == -1 ? -LONG_MIN : a1 / a2;
  }


Прошу экспертов поправить, если я где-то ошибаюсь.

P.S. исправил опечатку в примере с long((ulong)l1 + l2)

 
mktr8591:

Для себя я пришел к такому выводу. Для операций с int и long, чтобы избежать  UB из-за переполнения:

Просьба оформить в виде скрипта, где на числах показаны проблемные ситуации, которые обходятся.

Хочется иметь памятку в виде кода, где все лаконично продемонстрировано.


Сам сталкивался на MT4 и на MT5 с крутыми поворотами.

 
mktr8591:
А (i==i) можно?

Можно, но это уже дело статических анализаторов типа PVS Studio / CPP Check и аналогичных.

Обычные компиляторы более щадяще относятся к коду, хотя и увеличивают объем анализа.

 
fxsaber:

Просьба оформить в виде скрипта, где на числах показаны проблемные ситуации, которые обходятся.

Хочется иметь памятку в виде кода, где все лаконично продемонстрировано.


Сам сталкивался на MT4 и на MT5 с крутыми поворотами.

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

По стандарту C++ беззнаковые вычисления выполняются по модулю 2, т.е. при сложении UINT_MAX+2 итоговым результатом операции  будет UINT_MAX+2 mod (2^32) == 1, т.е. переход через 0. Это штатная ситуация для беззнаковых целых типов - не UB!

Но для знаковых целых типов такое же переполнение - уже UB в C++ и в mql (как сообщил Ilyas  https://www.mql5.com/ru/forum/371494/page29#comment_24202754).

По факту переполнение при сложении/вычитании и умножении int и long работает предсказуемо, например INT_MAX+1 всегда возвращает INT_MIN. Но тем не менее, из-за UB нет гарантии, что так будет всегда и на всех платформах (или я напрасно беспокоюсь и можно спокойно кодить UB?) .

Я знаю пока только один пример непредсказуемого поведения при делении INT_MIN/-1 (и LONG_MIN/-1) - они выдают непостоянные результаты.


Еще одна ситуация - для беззнакового ushort тоже может возникнуть UB. При умножении ushort*ushort может быть UB, т.к. ushort младше int и приводится к int; и может быть переполнение - только при умножении.


Вот примеры того, как можно обойти эти UB, с расширением результата операций до большей разрядности и без:

//По стандарту C++ беззнаковые вычисления выполняются по модулю 2, т.е. итоговым результатом будет UINT_MAX+2 mod (2^32) == 1
//Это штатная ситуация для беззнаковых целых типов
   Print(UINT_MAX + 2);                 //1            : переполнение, но не UB


//для знаковых целых такое же переполнение - уже UB.
   Print(INT_MAX + 2);                  //-2147483647    : переполнение и  UB!!!
   Print(long(INT_MAX) + 2    );        // 2147483649    : нет переполнения
   Print(int(long(INT_MAX) + 2));       // -2147483647   : переполнение при усечении по (int), но не UB!

   Print(INT_MAX / 2 * 3);              // -1073741827   : переполнение и  UB!!!
   Print((long)INT_MAX / 2 * 3);          //3221225469   : нет переполнения
   Print(int((long)INT_MAX / 2 * 3));     // -1073741827 :переполнение при усечении по (int), но не UB!

   int i = INT_MIN;
   int i2 = -1;
   Print(i * i2);                         // -2147483648  :переполнение и  UB!!!
//казалось бы, результат будет тот же
   Print(i / i2);                         // выдает разные результаты - то 0, то INT_MIN, то случайные числа : переполнение и  UB!!!
   Print((long)i / i2);                   //  2147483648 :  нет переполнения


   Print(ULONG_MAX + 2);                  // 1            : переполнение, но не UB

   Print(LONG_MAX + 2);                   // -9223372036854775807   : переполнение и  UB!!!
   Print(long((ulong)LONG_MAX + 2));      // -9223372036854775807   :  переполнение при усечении, но не UB!

   Print(LONG_MAX / 2 * 3);                 // -4611686018427387907  : переполнение и  UB!!!
   Print(long((ulong)LONG_MAX / 2 * 3));    // -4611686018427387907  : переполнение при усечении, но не UB!

   long l = LONG_MIN, l2 = -1;
   Print(l / l2);                                           // 99765774-случайное число   :переполнение и  UB!!!
   Print(l == LONG_MIN && l2 == -1 ? LONG_MIN : l / l2);   // -9223372036854775808 : "переполнение", но не UB

//особая ситуация - при умножении ushort*ushort может быть UB, т.к. ushort младше int и приводится к int
   Print(USHORT_MAX * 2);                    // 131070           : нет переполнения

   Print(USHORT_MAX * USHORT_MAX);           //-131071      : переполнение и  UB!!!
   Print((uint)USHORT_MAX * USHORT_MAX);     // 4294836225  : нет переполнения
   //или так:
   Print((long)USHORT_MAX * USHORT_MAX);     // 4294836225  : нет переполнения


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

Новая версия платформы MetaTrader 5 build 2980: Push-уведомления о торговых операциях
Новая версия платформы MetaTrader 5 build 2980: Push-уведомления о торговых операциях
  • 2021.08.20
  • www.mql5.com
В пятницу 18 июня 2021 года будет выпущена обновленная версия платформы MetaTrader 5...
 
mktr8591:
А (i==i) можно?

сравнение double с самой-же double часто используется как тест на валидность. (что оно не специальное представление)

оптимизатор не должен такое вырезать ни при каких условиях и флагах

 
Maxim Kuznetsov:

сравнение double с самой-же double часто используется как тест на валидность. (что оно не специальное представление)

оптимизатор не должен такое вырезать ни при каких условиях и флагах

да. но в контекст вопроса был - целые числа.
 
mktr8591:

Вот примеры того, как можно обойти эти UB, с расширением результата операций до большей разрядности и без:

Это все касается также и операции унарный минус с int и long.
Причина обращения: