Branch not taken on Linux [duplicate] - c++

This question already has answers here:
Do C99 signed integer types defined in stdint.h exhibit well-defined behaviour in case of an overflow?
(2 answers)
Closed 3 years ago.
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

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.

Related

Why doesn't the bit-shift of a variable and a number have the same result?

I'm shifting some bits and just realized that doing the operation using a variable doesn't have the same result as using a number. See the example below.
int a = 97;
int b = 0;
b = 1 << a;
printf("%d\n", b);
// 2
b = 1 << 97;
printf("%d\n", b);
// 0 - warning: shift count >= width of type [-Wshift-count-overflow]
Since the result of a left shift with a right operand larger than the length in bits of the left operand is undefined, any result is possible from the expression.
In the variable case (1 << a), since a is 97 (larger than the number of bits in an int), the most likely results are 1 << (97 % 32) == 1 << 1 == 2 or 0, typically depending on how the hardware (CPU) handles these shifts.
With a constant (1 << 97), the compiler knows you're shifting too far, issues the warning (which is not required), and defines the result as 0 (also not required).
The warning you are seeing is a compile time warning. Now, you can clearly see that your int b is a 32-bit variable which will be overflown if left-shifted 97 times. So, it's a valid concern. But the compiler can only detect this overflow for the constant number of shifts as it is evaluated during compilation and the compiler immediately knows that it'll overflow.
In case of variable number of shifts, the compiler isn't smart enough to know what value int a will posses when it'll come down to shifting. So, the compiler leaves it upto you.
The undefined behavior is outlined in the C++ standard here.
http://eel.is/c++draft/expr.shift
The behavior is undefined if the right operand is negative, or greater
than or equal to the width of the promoted left operand.
You'll get different results depending on compiler and optimization level. If you turn on optimization, the compiler will easily optimize out the first shifting operation and then make it 0 as well.
Why exactly does it act like that though? The x86 instruction for shifting by a variable is SAL (shift-arithmetic-left). You can see the instruction list for bit shifting operations here:
https://c9x.me/x86/html/file_module_x86_id_285.html
The one that would be used in an unoptimized build would be SAL r/m32, CL. The CL register is 8 bits, but the processor masks it to 5 bits internally:
The destination operand can be a register or a memory location. The count operand can be an immediate value or register CL. The count is masked to 5 bits, which limits the count range to 0 to 31. A special opcode encoding is provided for a count of 1.

Is this loop implicitly casting an int to size_t?

I ran into a bug in my program:
for (int i = 0; i < objArray.size() - 1; ++i)
In my case objArray.size() is an unsigned long long and an empty vector minus 1 equals about 18 quintillion. I was wondering, does the loop on every iteration have to cast an int to an unsigned long long? I checked the assembly and while using an int creates different code than size_t without optimisations, with -O2 specified it generates exactly the same assembly. Does this mean it's not implicitly casting?
I don't understand assembly, but the code it generated was:
test rcx, rcx
je .L32
add rdx, rax
and then :
cmp rdx, rax
jne .L28
This may be caused by a compiler optimization. The c++ standard says that overflow of signed integral types is undefined. In this case, i starts at 0. Supposing that i is not written to in the loop, the compiler can thus deduce, that i >= 0, since overflowing is undefined behaviour and can be pruned.
Normally, for an signed-unsigned comparison, the signed value would have to be converted to the unsigned type following the rules you can see here. These rules are the reason for the compiler warnings when comparing a signed and unsigned type (leading to confusion, e.g. -1 > 2U
is true). In this case, that doesn't matter though.
With the assumption i >= 0 and 2-complement signed types though, the compiler can safely reinterpret i as an unsigned long long since he knows the sign-bit is 0. That's what your assemly output shows.
Now, we can see that there is indeed a bug. Suppose objArray.size() - 1 does not fit into a positive signed int. This would cause i to overflow, thus cause undefined behaviour which is always bad news.
Lets dissect the code
for (int i = 0; i < objArray.size() - 1; ++i)
you are doing a comparison between and int and a size_t. The size( )-1 is an unsigned underflow when the array is empty and results in a value of std::numeric_limits::max( ). The comparison, will be signed/unsigned and use the type promotion rules as outlined here Signed/unsigned comparisons

Bit shifting and assignment [duplicate]

This question already has answers here:
Why doesn't left bit-shift, "<<", for 32-bit integers work as expected when used more than 32 times?
(10 answers)
Closed 9 years ago.
This is sort of driving me crazy.
int a = 0xffffffff;
int b = 32;
cout << (a << b) << "\n";
cout << (0xffffffff << 32) << "\n";
My output is
-1
0
Why am I not getting
0
0
Undefined behavior occurs when you shift a value by a number of bits which is not less than its size (e.g, 32 or more bits for a 32-bit integer). You've just encountered an example of that undefined behavior.
The short answer is that, since you're using an implementation with 32-bit int, the language standard says that a 32-bit shift is undefined behavior. Both the C standard (section 6.5.7) and the C++ standard (section 5.8) say
If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
But if you want to know why:
Many computers have an instruction that can shift the value in a register, but the hardware only handles shift values that are actually needed. For instance, when shifting a 32 bit word, only 5 bits are needed to represent a shift value of 0 ... 31 and so the hardware may ignore higher order bits, and does on *86 machines (except for the 8086). So that compiler implementations could just use the instruction without generating extra code to check whether the shift value is too big, the authors of the C Standard (many of whom represented compiler vendors) ruled that the result of shifting by larger amounts is undefined.
Your first shift is performed at run time and it encounters this situation ... only the low order 5 bits of b are considered by your machine, and they are 0, so no shift happens. Your second shift is done at compile time, and the compiler calculates the value differently and actually does the 32-bit shift.
If you want to shift by an amount that may be larger than the number of bits in the thing you're shifting, you need to check the range of the value yourself. One possible way to do that is
#define LEFT_SHIFT(a, b) ((b) >= CHAR_BIT * sizeof(a)? 0 : (a) << (b))
C++ standard says ::
If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.
As GCC has no options to handle shifts by negative amounts or by amounts outside the width of the type predictably or trap on them; they are always treated as undefined.
So behavior is not defined.

Result of (int)-2147483648 divided by (int)-1, using different compilers? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
As we all know, the smallest integer is different depending on the compiler, so I have this question: What's the result of (int)-2147483648 divided by (int)-1 you get when you use various compilers? For example, VC6.0, VS2010 etc.?
First let's assume we are in a 32-bit system with two's complement representation where INT_MIN value is the same as -INT_MAX - 1.
This expression:
(int) -2147483648 / (int) -1
is equivalent to
(int) -2147483648 / -1
as -1 is already of type int.
In a 32-bit two's complement system where INT_MAX is 2147483647, the value 2147483648 is of type long1) as it cannot be represented in an int. The value of -2147483648 is also of type long.
The long value -2147483648 can be represented in an int, and the value after the integer conversion of (int) -2147483648 is INT_MIN.
The original expression is then equivalent (under our assumptions) to:
INT_MIN / -1
and this expression is equivalent to INT_MAX + 1 which is not representable in an int. Indeed int type range goes from INT_MIN to INT_MAX. The expression is an integer overflow and integer overflow invokes undefined behavior in C.
(C99, 6.5p5) "If an exceptional condition occurs during the evaluation of an expression (that is, if the result is not mathematically defined or not in the range of representable values for its type), the behavior is undefined."
1) We implicitly assume LONG_MAX is > INT_MAX, otherwise the value 2147483648 is of type long long.

C++ underflow and overflow

Hi I am new in here so please let me know if anything is wrong and I will try to better the next time .
I am trying to understand how underflow and overflow works in C++ .My understanding is if a variable's range is exceeded it will start from the other end of the range . Thus if minimum of short is -32768 and if we do a -1 to it the new value should be SHRT_MAX .(32767)
Here is my code:
#include<iostream.h>
#include<limits.h>
#include<conio.h>
int main ( void )
{
int testpositive =INT_MIN ;
short testnegative = SHRT_MIN ;
cout<< SHRT_MIN<<"\n";
cout << testnegative-1<<"\n";
cout << INT_MIN << "\n";
cout << testpositive-1 << "\n";
cout<<testpositive-2;
getch();
return 0;
}
The exact behavior on overflow/underflow is only specified for unsigned types.
Unsigned integers shall obey the laws of arithmetic modulo 2^n where n is the number of bits in the value representation of that particular size of integer.
Source: Draft N3690 §3.9.1 sentence 4
This implies that unsigned arithmetic does not overflow because a result that cannot be represented by the resulting
unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the
resulting unsigned integer type.
Source: Draft N3690 Note 47 for §3.9.1
For normal signed integer types instead the C++ standard simply says than anything can happen.
If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined
Source: Draft N3690 §5 sentence 4
If we're talking about x86 processor (or most other modern processors) indeed the behavior is exactly what you describe and for the CPU there is no difference between a signed value or an unsigned value (there are signed and unsigned operations, but the value themselves are just bits).
Note that compilers can assume (and most modern optimizing compilers actually DO assume) that no signed integer overflow can occur in a correct program and for example in code like:
int do_something();
int do_something_else();
void foo() {
int x = do_something();
int y = x + 1;
if (x < y) {
do_something();
} else {
do_something_else();
}
}
a compiler is free to skip the test and the else branch in the generated code completely because in a valid program a signed int x is always less than x+1 (as signed overflow cannot be considered valid behavior).
If you replace int with unsigned int however the compiler must generate code for the test and for the else branch because for unsigned types it's possible that x > x+1.
For example clang compiles the code for foo to
foo(): # #foo()
push rax
call do_something()
pop rax
jmp do_something() # TAILCALL
where you can see that the ode just calls do_something twice (except for the strange handling of rax) and no mention of do_something_else is actually present. More or less the same code is generated by gcc.
Signed overflows are undefined behavior in C++.
For example:
INT_MIN - 1
-INT_MIN
are expressions that invoke undefined behavior.
SHRT_MIN - 1 and -SHRT_MIN are not undefined behavior in an environment with 16-bit short and 32-bit int because with integer promotions the operand is promoted to int first. In an environment with 16-bit short and int, these expressions are also undefined behavior.
Typically yes. But since this is C++, and C++ is regulated by the C++ standard, you must know that overflows are undefined behavior.
Although what you stated probably applies on most platforms, it's in no way guaranteed, so don't rely on it.
The new value need not be SHRT_MAX it is undefined.