INT_MIN%-1是否会产生未定义的行为?

R..*_*R.. 24 c gcc overflow division modulo

gcc SIGFPE为以下代码生成浮动代码:

#include <limits.h>
int x = -1;
int main()
{
    return INT_MIN % x;
}
Run Code Online (Sandbox Code Playgroud)

但是,我在标准中找不到此代码调用未定义或实现定义的行为的语句.据我所知,它需要返回0.这是gcc中的错误还是我错过了标准的一些特殊例外?

orl*_*rlp 16

查看gcc生成的汇编代码(x在程序集的前面定义为-1):

movl    x, %ecx
movl    $-2147483648, %eax
movl    %eax, %edx
sarl    $31, %edx
idivl   %ecx
Run Code Online (Sandbox Code Playgroud)

第一个计算指令sarl右移-214748364831位.这导致-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

  • @R.:嗯,等一下,标准说"如果商a/b是可表示的",这在INT_MIN%-1中不是这种情况.它没有说明发生这种情况时会发生什么,因此它是UB. (5认同)
  • 标准说"如果商a/b在结果类型中可表示,(a/b)*b + a%b等于a." 当语句*不可表示时,此语句不限制行为,这意味着先前的语句仍然有效.前面的陈述声明'%`产生余数.在这种情况下,其余部分为0. (3认同)
  • 您关于*SIGFPE`*如何发生的信息是正确的.我关心的是这是否正确.我从未要求编译器计算`INT_MIN/-1`(由于它溢出会明显导致UB),只是为了计算`INT_MIN%-1',我在标准中找不到后者调用UB的语句.仍然是一个信息丰富的答案. (2认同)

Jen*_*edt 16

您可能认为这可以被视为实际标准中的错误.在目前的草案解决了这个问题:

如果商a/b是可表示的,则表达式(a/b)*b + a%b应等于a; 否则,a/b和%b的行为都是未定义的.