В OFront воплощение функции деления было сделано с ошибками. Они остаются в
OFront+ и в
vocКод:
LONGINT SYSTEM_DIV(LONGINT x, LONGINT y) // INT64 SYSTEM_DIV(INT64 x, INT64 y)
{
if (x == 0) return 0;
if (x >= 0)
if (y >= 0) {return x/y;}
else {return -((x-y-1)/(-y));}
else
if (y >= 0) {return -((y-x-1)/y);}
else {return (-x)/(-y);}
}
При условии ( (x < 0) # (y < 0) ) & ( ABS(x) > MAX(LONGINT) - ABS(y) ) будет переполнение несмотря на допустимые для деления входные параметры. Согласно стандарту Си переполнение целых чисел со знаком является неопределённым поведением, что не позволяет предсказать поведение программы для общего случая - возможна и аварийная остановка. При использовании флага компилятора -fwrapv в gcc и clang переполнение будет проводится в соответствии с машинными вычислениями в дополнительном коде. Но и тогда результат во многих случаях будет некорректным.
Также, из-за
дополнительного кода будет выполняться некорректное равенство MIN(LONGINT) DIV (-1) = MIN(LONGINT) . clang для такого деления может сгенерировать аварийное завершение несмотря на флаг -fwrapv, что можно рассматривать как преимущество для диагностики.
В
CPFront нет проблемы с переполнением
Код:
INTEGER SYSTEM_DIV(INTEGER x, INTEGER y)
{
if (y > 0) {
if (x < 0) return ~(~x / y);
else return x / y;
} else if (y < 0) {
if (x > 0) return ~((x - 1) / -y);
else return -x / -y;
} else {
__HALT(-5);
}
}
Ещё одним преимуществом этого варианта в том, что в нём явная проверка деления на 0. В Си полагаться на то, что при делении на 0 произойдёт аварийная остановка в общем случае нельзя, потому что это тоже неопределённое поведение. Тем не менее, в решении для КП тоже есть проблема с делением минимального значения на -1, а трюки с побитовым отрицанием жёстко завязаны на дополнительный код.