voidOnStart()
{
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);
//orint i3=int((long)i1*i2);
//or int i3=int((long)i1/i2); //check i2!=0 before
2. Для операций +,-,* с long надо привести одну из переменных к ulong (т.к. эти операции выполняются одинаково для ulong и long кроме флагов - переполняния и знака и тп), а потом результат к long.
3. При делении long/long переполнение, насколько я знаю, может возникнуть только при LONG_MIN/-1 (и результатом возвращается 0). Поэтому нужна такая конструкция:
Просьба оформить в виде скрипта, где на числах показаны проблемные ситуации, которые обходятся.
Хочется иметь памятку в виде кода, где все лаконично продемонстрировано.
Сам сталкивался на MT4 и на MT5 с крутыми поворотами.
Как известно, если результат операции с целыми числами превышает верхнюю границу допустимого диапазона значения для типа, происходит целочисленное переполнение.
По стандарту C++ беззнаковые вычисления выполняются по модулю 2, т.е. при сложении UINT_MAX+2 итоговым результатом операции будет UINT_MAX+2 mod (2^32) == 1, т.е. переход через 0. Это штатная ситуация для беззнаковых целых типов - не UB!
По факту переполнение при сложении/вычитании и умножении 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 : переполнение, но не UBPrint(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 и приводится к intPrint(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. В общем-то эти вещи общеизвестные. Есть еще несколько "подводных граблей" с целыми, про которые мало где упомяняют и новички на них рано или поздно наступят. Хочу написать отдельный пост, но позже.
это лишь проверка на допустимые значения для типа
мой пример тож компилятор прекрасно отследит:
" просто сравнивает на эквивалентность типов и возможности кастинга к другим типам" - это и значит, что компилятор прекрасно знает тип переменной.
например такой код выдает предупреждение компилятора:
Так что предложение fxsaber не лишено оснований.
Пример синтетический и полностью оптимизируется компилятором методом распространения констант.
То есть, вместо переменной будет чистая константа в условии. И такое вырожденное условие можно на этапе компиляции отловить.
Пример синтетический и полностью оптимизируется компилятором методом распространения констант.
То есть, вместо переменной будет чистая константа в условии. И такое вырожденное условие можно на этапе компиляции отловить.
Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий
Новая версия платформы MetaTrader 5 build 2980: Push-уведомления о торговых операциях
Igor Makanu, 2021.08.23 21:29
проверил совет из статьи, на Вашем примере
так работает при компиляции по F5
К сожалению, так работает совершенно неправильно. добавление +0u приводит min к uint, и -1 тоже приводится к uint, превращаясь в UINT_MAX, поэтому получается выражение
что равно 0 для любого min типа int. (в вашем примере и печатается "i = 0").
Для себя я пришел к такому выводу. Для операций с int и long, чтобы избежать UB из-за переполнения:
1. Для операций +,-,*,"/" с двуми переменными int проще всего привести одну из них к long. А потом, если надо, конвертировать результат в int:
2. Для операций +,-,* с long надо привести одну из переменных к ulong (т.к. эти операции выполняются одинаково для ulong и long кроме флагов - переполняния и знака и тп), а потом результат к long.
3. При делении long/long переполнение, насколько я знаю, может возникнуть только при LONG_MIN/-1 (и результатом возвращается 0). Поэтому нужна такая конструкция:
Удобнее, наверно, сделать функцию (пригодная и для int тоже):
Прошу экспертов поправить, если я где-то ошибаюсь.
P.S. исправил опечатку в примере с long((ulong)l1 + l2)
Для себя я пришел к такому выводу. Для операций с int и long, чтобы избежать UB из-за переполнения:
Просьба оформить в виде скрипта, где на числах показаны проблемные ситуации, которые обходятся.
Хочется иметь памятку в виде кода, где все лаконично продемонстрировано.
Сам сталкивался на MT4 и на MT5 с крутыми поворотами.
А (i==i) можно?
Можно, но это уже дело статических анализаторов типа PVS Studio / CPP Check и аналогичных.
Обычные компиляторы более щадяще относятся к коду, хотя и увеличивают объем анализа.
Просьба оформить в виде скрипта, где на числах показаны проблемные ситуации, которые обходятся.
Хочется иметь памятку в виде кода, где все лаконично продемонстрировано.
Сам сталкивался на 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, с расширением результата операций до большей разрядности и без:
P.S. В общем-то эти вещи общеизвестные. Есть еще несколько "подводных граблей" с целыми, про которые мало где упомяняют и новички на них рано или поздно наступят. Хочу написать отдельный пост, но позже.
А (i==i) можно?
сравнение double с самой-же double часто используется как тест на валидность. (что оно не специальное представление)
оптимизатор не должен такое вырезать ни при каких условиях и флагах
сравнение double с самой-же double часто используется как тест на валидность. (что оно не специальное представление)
оптимизатор не должен такое вырезать ни при каких условиях и флагах
Вот примеры того, как можно обойти эти UB, с расширением результата операций до большей разрядности и без: