Ответ 1
Вероятно, вы правы, что это можно считать ошибкой в реальном стандарте. текущий проект решает эту проблему:
Если частное a/b представимо, выражение (a/b) * b + a% b должно равен a; в противном случае поведение оба a/b и a% b undefined.
gcc генерирует плавающий код, который вызывает SIGFPE
для следующего кода:
#include <limits.h>
int x = -1;
int main()
{
return INT_MIN % x;
}
Однако я не могу найти инструкцию в стандарте, что этот код вызывает undefined или поведение, определяемое реализацией. Насколько я могу судить, ему нужно было вернуть 0. Является ли это ошибкой в gcc или мне не хватает какого-то особого исключения, которое делает стандарт?
Вероятно, вы правы, что это можно считать ошибкой в реальном стандарте. текущий проект решает эту проблему:
Если частное a/b представимо, выражение (a/b) * b + a% b должно равен a; в противном случае поведение оба a/b и a% b undefined.
Взгляд на код сборки, сгенерированный gcc (x определяется как -1 ранее в сборке):
movl x, %ecx
movl $-2147483648, %eax
movl %eax, %edx
sarl $31, %edx
idivl %ecx
Первая вычислительная инструкция sarl
, правые сдвиги -2147483648
31 бит. Это приводит к -1
, который помещается в %edx
.
Далее выполняется idivl
. Это подписанная операция. Позвольте мне привести описание:
Разделяет содержимое двойного слова, содержащегося в объединенных регистрах% edx:% eax, на значение в указанном регистре или в памяти.
Итак, -1:-2147483648 / -1
- это деление, которое происходит. -1:-2147483648
, интерпретируемое как двойное слово, равно -2147483648
(на двухкомпонентной машине). Теперь происходит -2147483648 / -1
, который возвращает 2147483648
. БУМ! Это еще один INT_MAX
.
О том, почему вопрос, это ошибка в gcc или я пропустил какое-то специальное исключение, которое делает стандарт?
В стандарте C99 это неявное UB (§6.5.5/6):
... результат оператора/является алгебраическим фактором с любой дробной частью, отброшенной .88) Если фактор a/b является представимым, выражение (a/b) * b + a% b должно быть равно a.
INT_MIN / -1
не может быть представлен, таким образом, это UB.
Однако в C89 оператор% определяется реализацией и может ли обсуждаться ошибка компилятора или нет. Однако проблема указана в gcc: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30484
Тот же вопрос задается здесь как Отчет о дефектах
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#614
К сожалению, я не вижу, чтобы он прямо заявил в части резолюции, что он должен производить UB. Деление действительно произведет UB, но для оператора %
это не очевидно.
Результат работы модуля с отрицательными операндами оставлен в реализации, определенный в C89, и определен в C99 в § 6.5.5/6:
& hellip, результатом оператора
/
является алгебраическое отношение с любой дробной частью, отброшенной. 88) Если факторa/b
является представимым, выражение(a/b)*b
+a%b
должен быть равенa
.88) Это часто называют "усечением к нулю".
Для представления с двумя дополнениями INT_MIN / -1
равно INT_MAX + 1
, поэтому он не может быть представлен как int
без упаковки, и я предполагаю, что реализация решила оставить его взрывоопасным.