В последнее время большое внимание уделяется тому, что подписанное целочисленное переполнение официально undefined в C и С++. Однако данная реализация может решить ее определить; в С++ реализация может установить std::numeric_limits<signed T>::is_modulo
в true
, чтобы указать, что знаковое целочисленное переполнение корректно определено для этого типа и обертывается как целые числа без знака.
Visual С++ устанавливает std::numeric_limits<signed int>::is_modulo
в true
. Это вряд ли было надежным показателем, поскольку GCC установил это значение в течение многих лет и имеет undefined подписанное переполнение. Я никогда не сталкивался с ситуацией, в которой оптимизатор Visual С++ сделал что-либо, но при условии, что поведение wraparound для целых чисел со знаком - до этой недели.
Я нашел случай, когда оптимизатор испустил код сборки x86-64, который действовал неправильно, если значение точно INT_MAX
было передано определенной функции. Я не могу сказать, является ли это ошибкой, потому что Visual С++, похоже, не определяет, считается ли объявленное целочисленное переполнение. Так что мне интересно, это должно быть определено в Visual С++?
EDIT: я нашел это, когда читал о неприятной ошибке в Visual С++ 2013 Update 2, которая не была в обновлении 1, где следующий цикл генерирует плохой машинный код, если оптимизация включена:
void func (int *b, int n)
{
for (int i = 0; i < n; i++)
b[i * (n + 1)] = 1;
}
Ошибка Update 2 приводит к повторной строке, генерирующей код, как если бы она была b[i] = 1;
, что явно неверно. Он превратился в rep stosd
.
Что было действительно интересно, так это то, что в предыдущей версии было обновлено, обновление 1. Это сгенерированный код, который неправильно обрабатывал случай, когда n
в точности равнялся INT_MAX
. В частности, если n
были INT_MAX
, умножение будет действовать так, как если бы n
были long long
вместо int
- другими словами, добавление n + 1
не привело бы к тому, что результат стал INT_MIN
как он должен.
Это был код сборки в обновлении 1:
movsxd rax, edx ; RDX = 0x000000007FFFFFFF; RAX = 0x000000007FFFFFFF.
test edx, edx
jle short locret_76 ; Branch not taken, because EDX is nonnegative.
lea rdx, ds:4[rax*4] ; RDX = RAX * 4 + 4; RDX becomes 0x0000000200000000.
nop ; But it wrong. RDX should now be 0xFFFFFFFE00000000.
loc_68:
mov dword ptr [rcx], 1
add rcx, rdx
dec rax
jnz short loc_68
locret_76:
retn
Проблема в том, что я не знаю, является ли это ошибкой компилятора - в GCC и Clang это не будет ошибкой компилятора, потому что эти компиляторы считают, что подписанное целочисленное переполнение/нижнее течение undefined. Является ли это ошибкой в Visual С++, зависит от того, считает ли Visual С++ подписанное целочисленное переполнение /underflow undefined.
Каждый другой случай, который я видел помимо этого, показал, что Visual С++ рассматривает подписанный переполнение/нижний поток для определения, следовательно, тайну.