24

gcc generates floating code that raises SIGFPE for the following code:

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

However I can find no statement in the standard that this code invokes undefined or implementation-defined behavior. As far as I can tell, it's required to return 0. Is this a bug in gcc or am I missing some special exception the standard makes?

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711

5 Answers5

17

You are probably right that this can be considered as a bug in the actual standard. The current draft addresses this problem:

If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a; otherwise, the behavior of both a/b and a%b is undefined.

caf
  • 233,326
  • 40
  • 323
  • 462
Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
16

Looking at the assembly code generated by gcc (x is defined as -1 earlier in the assembly):

movl    x, %ecx
movl    $-2147483648, %eax
movl    %eax, %edx
sarl    $31, %edx
idivl   %ecx

The first computational instruction, sarl, right shifts -2147483648 31 bits. This results in -1 which is put in %edx.

Next idivl is executed. This is a signed operation. Let me quote the description:

Divides the contents of the double-word contained in the combined %edx:%eax registers by the value in the register or memory location specified.

So -1:-2147483648 / -1 is the division that happens. -1:-2147483648 interpreted as a double word equals -2147483648 (on a two's complement machine). Now -2147483648 / -1 happens which returns 2147483648. BOOM! That's one more then INT_MAX.


About the why question, is this a bug in gcc or am I missing some special exception the standard makes?

In the C99 standard this is implicit UB (§6.5.5/6):

…the result of the / operator is the algebraic quotient with any fractional part discarded.88) If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a.

INT_MIN / -1 cannot be represented, thus this is UB.

In C89 however the % operator is implementation defined and whether this is a compiler bug or not can be debated. The issue is listed at gcc however: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=30484

orlp
  • 112,504
  • 36
  • 218
  • 315
  • 2
    Your information about *how* the `SIGFPE` is happening is correct. My concern though is whether this is correct. I never asked the compiler to compute `INT_MIN/-1` (which would clearly result in UB since it overflows), just to compute `INT_MIN%-1`, and I can find no statement in the standard that the latter invokes UB. Still, an informative answer. – R.. GitHub STOP HELPING ICE May 08 '11 at 01:55
  • Regarding your citation of 6.5.5/6: in the previous paragraph (5), it reads "the result of the % operator is the remainder". Paragraph 6 clarifies that when "the remainder" could be ambiguous, but "the remainder" is not ambiguous when `a` is a whole multiple of `b`; in this case the unique remainder is 0. – R.. GitHub STOP HELPING ICE May 08 '11 at 02:11
  • 5
    @R.: Hmm, wait a sec, the standard states "If the quotient a/b is representable", which is not the case in INT_MIN % -1. It doesn't state what should happen when this happens, thus it's UB. – orlp May 08 '11 at 02:19
  • 1
    It already stated in paragraph 5 that the result of `%` is "the remainder". As written, paragraph 6 merely clarifies which "the remainder" it's talking about. I think you may be right that the *intent* was not to define any behavior for `INT_MAX%-1`, but I don't see how the present language would accomplish that since it already defined it above... – R.. GitHub STOP HELPING ICE May 08 '11 at 03:54
  • 3
    The standard says "if the quotient a/b is representable in the type of the result, (a/b)*b + a%b is equal to a." This statement does not restrict the behavior in situations when the quotient is *not* representable, which means that the previous statements remain in effect. The previous statements state that `%` produces the remainder. And the remainder in this case is 0. – AnT stands with Russia May 08 '11 at 16:13
9

It's the CPU's fault.

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • Thanks for the informative link. At least it looks like people are mostly in agreement that it's a bug, but the "leave it broken by default and require a special option to fix it" approach sounds ridiculous... – R.. GitHub STOP HELPING ICE May 08 '11 at 01:59
  • Is there any simple bitwise/arithmetic expression that evaluates to `x` if `x!=-1` and `1` if `x==1`? If so, the compiler could apply this to the denominator before performing `%` with a variable denominator when `/` is not also being performed with the same denominator. – R.. GitHub STOP HELPING ICE May 08 '11 at 02:03
  • @R..: "off by default but on in standard-conforming modes" sounds alright, though, and that's what's suggested on the bug. `gcc` is not a conforming implementation, nor does it claim to be. `gcc -std=c99` isn't entirely conforming either, but it does attempt to be... – Steve Jessop May 08 '11 at 09:03
4

The same question is asked here as a Defect Report

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#614

Unfortunately I don't see it stated explicitly in the resolution part that it should produce UB. The division would indeed produce UB, but for the % operator it is not obvious.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
3

The result of the modulus operation with negative operands is left implementation-defined in C89, and defined in C99 by §6.5.5/6:

…the result of the / operator is the algebraic quotient with any fractional part discarded.88) If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a.

88) This is often called "truncation toward zero".

For a two's-complement representation, INT_MIN / -1 is equal to INT_MAX + 1, so it's not representable as an int without wrapping, and I guess the implementation elects to leave it explosive.

Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
  • The `/` operator does not appear in my program, however. And regardless of how you define the result with negative operands ("rounding" towards 0 or down), -1 evenly divides anything and thus the only possible remainder is zero, mathematically. – R.. GitHub STOP HELPING ICE May 08 '11 at 01:56
  • @R..: I was just citing a reference for the behaviour with respect to negative operands. The computation fails as GCC implements it, so it's basically a bug, if you need me to say it in so many words. – Jon Purdy May 08 '11 at 02:59
  • 3
    @R..: To put it another way, `%` is defined only when `/` is defined, and since `/` is here undefined, `%` is also undefined, even though it probably shouldn't be. – Jon Purdy May 08 '11 at 08:09