2

The below code works as expected on Windows but when built with Clang 6.0 and running on an Ubuntu server it does not work. The CurAnimIndex is an int32 and has the value 2147483647 (max int). I would expect it to enter the branch since the value of CurAnimIndex after the increment should be a negative number, however it does not.

CurAnimIndex++;
if (CurAnimIndex >= AnimSelectorDatas.Num() || CurAnimIndex < 0)
{
    CurAnimIndex = 0;
}


0x000000000411a12f  mov    0x0(%r13),%eax 
0x000000000411a133  lea    0x1(%rax),%ecx 
0x000000000411a136  movslq 0x10(%r13),%r15 
0x000000000411a13a  xor    %ebp,%ebp 
0x000000000411a13c  cmp    %r15d,%ecx 
0x000000000411a13f  cmovge %ebp,%ecx 
0x000000000411a142  cmp    $0xffffffff,%eax 
0x000000000411a145  cmovl  %ebp,%ecx 
0x000000000411a148  mov    %ecx,0x0(%r13) 
0x000000000411a14c  mov    0x8(%r13),%r12 enter code here
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
0xBADF00
  • 1,028
  • 12
  • 26
  • 4
    Signed overflow causes undefined behavior. The compiler has every right to assume it never happens. – HolyBlackCat Jan 14 '20 at 16:08
  • [Possible duplicate](https://stackoverflow.com/questions/16188263/is-signed-integer-overflow-still-undefined-behavior-in-c). Also, code that relies on "wrap-around" is dubious, at best. – PaulMcKenzie Jan 14 '20 at 16:11
  • @HolyBlackCat thanks I did not know that, if you post this as an answer I will accept it – 0xBADF00 Jan 14 '20 at 16:17
  • 1
    I'd rather close as a dupe of [Do C99 signed integer types defined in stdint.h exhibit well-defined behaviour in case of an overflow?](https://stackoverflow.com/questions/9367204/do-c99-signed-integer-types-defined-in-stdint-h-exhibit-well-defined-behaviour-i) – HolyBlackCat Jan 14 '20 at 16:25
  • @HolyBlackCat sure you can close it as duplicate – 0xBADF00 Jan 14 '20 at 16:26
  • To force signed integer overflow to be well-defined, use `-fwrapw` compiler option (obviously check docs first before adding it to your build). – hyde Jan 14 '20 at 17:11

1 Answers1

3
CurAnimIndex++

The CurAnimIndex is an int32 and has the value 2147483647 (max int). I would expect it to enter the branch since the value of CurAnimIndex after the increment should be a negative number

2147483647 is a positive number. Why would you expect that incrementing a positive number would yield a negative one? That doesn't happen in normal arithmetic. The compiler knows this and optimises according to that knowledge. If the initial value of CurAnimIndex has been proven to be at least -1, then the check CurAnimIndex < 0 is known always to be false and can be optimised away.

Maybe your expectation is related to the fact that the operation overflows the maximum representable value. That expectation is misplaced because signed overflow isn't guaranteed to have such behaviour. In fact, signed overflow isn't guaranteed to have any particular behaviour. The behaviour of the program is undefined.

A correct way to do this is to first check whether the number is equal to maximum representable value and only increment if it isn't.

Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • I don't think "normal arithmetic" has anything to do with this. Compiler optimizes this, because standard says integer wrap-around is *Undefined Behavior*, so compiler can do what ever it damn well pleases. Nothing "normal" about it. Binary addition with fixed number of bits in 2's complement normally wraps around. – hyde Jan 14 '20 at 17:10
  • @hyde Normal arithmetic is there to disambiguate from modular arithmetic where it is completely normal for the result of adding two numbers to be smaller than the operands. Signed addition in C++ is not modular, hence the clarification of what can be expected in normal arithmetic. – eerorika Jan 14 '20 at 17:12
  • My point is, that incrementing 2147483647 in the situation of the question specifically does not yield what you'd expect in normal arithmetic. – hyde Jan 14 '20 at 17:47
  • @hyde `specifically does not yield what you'd expect in normal arithmetic` It *can* yield what you'd expect in normal arithmetic. It can also yield what OP expected. Or yield neither. Signed arithmetic has normal result for all operations that don't have undefined behaviour. Because the compiler can assume that there is no UB, it can assume that the result is normal, whether it is or isn't (latter being always UB). – eerorika Jan 14 '20 at 17:54
  • Result is "undefined behavior", to me decidedly un-arithmetic, even un-mathematical and more just generally philosophical. Incidentally, looking at the assembly code, `CurAnimIndex` *will indeed be negative* afterwards for overflow (if 0 assignment is not done), that value is just not used in the comparison, because of UB. (Disclaimer, my assembly is a bit rusty.) – hyde Jan 14 '20 at 19:22