Visual C++ generates DIV instead of IDIV (x86, integer arithmetic) - c++

I'm working with Visual C++ 2008 here (9.x) and I was preparing a fixed point value when I ran into the compiler generating a DIV instead of an IDIV. I collapsed the code into a tiny piece to exactly reproduce:
short a = -255;
short divisor16 = 640; // unsigned, 16-bit
unsigned int divisor32 = 640; // unsigned, 32-bit
unsigned short s_divisor16 = 640; // signed, 16-bit
int s_divisor32 = 640; // signed, 32-bit
int16_t test1 = (a<<8)/divisor16; // == -102, generates IDIV -> OK
int16_t test2 = (a<<8)/s_divisor16; // == -102, generates IDIV -> OK
int16_t test3 = (a<<8)/divisor32; // == bogus, generates DIV -> FAIL!
int16_t test4 = (a<<8)/s_divisor32; // == -102, generates IDIV -> OK
int bitte_ein_breakpoint=1;
I won't bother you with the simple disassembly.
Now instead of taking the shortcut and just changing the divisor's type (it is a function parameter, unsigned int numPixels), I wonder what makes the compiler pick DIV over IDIV in the third (test3) case, since it does not do so with an unsigned 16-bit divisor and there really isn't anything that would call for unsigned arithmetic anyway. At least that's what I think and I hope I'm wrong :)

The code that is generated for the / operator depends on the operands.
First, the expression (a << 8) has type int, since the integer promotions are performed on each of the operands (ISO C99, 6.5.7p3), and then the operation is int << int, which results in an int.
Now there are four expressions:
int / short: the right hand side is promoted to int, therefore the idiv instruction.
int / unsigned short: the right hand side is promoted to int, therefore the idiv instruction.
int / unsigned int: the left hand side is promoted to unsigned int, therefore the div instruction.
int / int: nothing is promoted, therefore the idiv instruction is appropriate.
The integer promotions are defined in ISO C99 6.3.1.1p3:
If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions..

Left-shifting a negative value results in undefined behaviour. So I'm not sure you can draw many conclusions from what the compiler chooses to do in this scenario.

Related

signed double to unsigned byte: ARM64 versus Win64

For some legacy reasons I have code that casts a double to unsigned byte and we are seeing much difference between the two platforms. Short of doing - "don't try to stuff a signed value into unsigned value; don't try to stuff a double value in integer", is there something else that can be done?
unsigned char newR1 = -40.16;
Value of newR1 is 216 on windows (as we expected from a long time); but on ARM64 it is 0.
Disassembly on Win:
00007FF75E388818 cvttsd2si eax,mmword ptr [R]
00007FF75E38881D mov byte ptr [newR1],al
On ARM64
00007FF6F9E800DC ldr d16,[sp,#0x38 |#0x38 ]
00007FF6F9E800E0 fcvtzu w8,d16
00007FF6F9E800E4 uxtb w8,w8
00007FF6F9E800E8 strb w8,[sp,#0x43 |#0x43 ]
Will try these as well, but just wanted some other opinions
unsigned char newR1 = -40.16;
unsigned char newR2 = (int)-40.16;
unsigned char newR3 = (unsigned char)-40.16;
unsigned char newR4 = static_cast<int>(-40.16);
or may be
int i = -40.16;
unsigned char c = i;
What the C standard says (and there's similar text in the C++ one):
When a finite value of real floating type is converted to an integer
type other than _Bool, the fractional part is discarded (i.e., the
value is truncated toward zero). If the value of the integral part
cannot be represented by the integer type, the behavior is undefined.
So, getting 216 out of -40.16 with a single cast from double to unsigned char is already UB. In fact, getting any result in this case is UB. Which is why the compiler is free to produce anything and not 216 that you desire.
You may want to do two casts:
(unsigned char)(int)-40.16
Again, the first cast (to int) is still subject to the above restriction I quoted.

Will implicit type conversion not happen if I divide by 1?

#include <stdio.h>
int main()
{
unsigned int count=1;
signed int val = -20;
signed int div=(val/count);
signed int div1=(val/(signed int)count);
printf("div %d div1 %d \n",div,div1);
return 0;
}
output :
div -20 div1 -20
But if count = 5 then the output :
div 858993455 div1 -4
In count=5 case signed int has been implicitly converted to unsigned int, why not for count=1
signed int div=(val/count);
If one of operands is int and another one is unsigned int, the int one is converted to unsigned int. So here val is converted to unsigned int, then divided by count, then the result is converted back to int.
That is, this expression is evaluated as
int div = (int)((unsigned int)val / count);
So when count == 1, the result remains the same, but when count == 5 the result of (unsigned int)val / count becomes less than INT_MAX, so when converted back to int it doesn't change its (big positive) value.
Note that strictly speaking, even if count == 1 the result doesn't have to be -20, because the result of conversion from (unsigned int)-20 to int is implementation-defined.
There is no such thing as "implicit typecast", typecast refers to explicitly changing the type of an operand. The proper term is implicit (type) conversion.
The fundamentals of C state that the compiler is free to order around or optimize your program as it pleases, as long as it doesn't change the outcome of the program.
So even if the compiler spots that a division by 1 doesn't make sense and can get optimized away, it must still take the potential side-effects caused by the implicit type conversion in account: it cannot optimize those away, because the programmer might have been intentionally relying on them.
In your specific case, signed int div=(val/count) would enforce val to get implicitly converted to unsigned type. But it doesn't really matter, as you show the results back into a signed type and anything divided by 1 will remain unchanged anyhow. So the compiler can therefore optimize the whole thing away as the result would have been the same no matter if unsigned or signed arithmetic was used.
If you divide by 5 though, the results turn very different between -20/5 = -4 and 0xFFFFFFEC/5 = 0xFFFFFFFC. So then the compiler is not allowed to optimize away the implict conversion, as it affects the result.
Therefore the programmer has to know the implicit type conversion rules to tell what will actually happen between the lines of their own source code.
This is the the usual arithmetic conversions. you can find rule there.
So actually, the first result is (unsigned)-4. use complement rule, it will be 858993455.
you can also reference Implicit conversions

what does it mean to bitwise left shift an unsigned char with 16

i am reading a .cpp file containing a unsigned char variable, it's trying the bitwise left shift 16 bits, since an unsigned char is composed of 8 bits, left shift 16 bits will erase all the bits and fill it with eight 0s.
unsigned char byte=0xff; byte << 16;
When you shift a value,
unsigned char x = ...;
int y = x << 16;
The type of x is promoted to int if unsigned char fits in an int (most systems), or to unsigned if unsigned char does not fit in an int (rare1). As long as your int is 25 bits wide or wider, then no data will be discarded2.
Note that this is completely unrelated to the fact that 16 has type int.
/* All three are exactly equivalent */
x << 16;
x << 16u;
x << (unsigned char) 16;
Source: from n1516 (C99 draft):
§6.5.7 paragraph 3: Bitwise Shift Operators
The integer promotions are performed on each of the operands. The type of the result is
that of the promoted left operand.
§6.3.1.1 paragraph 2: Boolean, characters, and integers
If an int can represent all values of the original type (as restricted by the width, for a
bit-field), the value is converted to an int; otherwise, it is converted to an unsigned
int. These are called the integer promotions.
Footnotes:
1: Some DSP chips as well as certain Cray supercomputers are known to have sizeof(char) == sizeof(int). This simplifies design of the processor's load-store unit at the cost of additional memory consumption.
2: If your left shift is promoted to int and then overflows the int, this is undefined behavior (demons may fly out your nose). By comparison, overflowing an unsigned is always well-defined, so bit shifts should usually be done on unsigned types.
If char fits inside int, it will be promoted to an int and the result will be as you expect. If that is not the case it is undefined behavior according to the standard, and will probably emit a compile warning. From the standard:
The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. 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.

Implicit type casts in expressions with bit shifts

In the code below, why 1-byte anUChar is automatically converted into 4 bytes to produce the desired result 0x300 (instead of 0x0 if anUChar would remain 1 byte in size):
unsigned char anUChar = 0xc0; // only the two most significant bits are set
int anInt = anUChar << 2; // 0x300 (correct)
But in this code, aimed at a 64-bit result, no automatic conversion into 8 bytes happens:
unsigned int anUInt = 0xc0000000; // only the two most significant bits are set
long long aLongLong = anUInt << 2; // 0x0 (wrong, means no conversion occurred)
And only placing an explicit type cast works:
unsigned int anUInt = 0xc0000000;
long long aLongLong = (long long)anUInt << 2; // 0x300000000 (correct)
And most importantly, would this behavior be the same in a program that targets 64-bit machines?
By the way, which of the two is most right and portable: (type)var << 1 or ((type)var) << 1?
char always gets promoted to int during arithmetic. I think this is specified behavior in the C standard.
However, int is not automatically promoted to long long.
Under some situations, some compilers (Visual Studio) will actually warn you about this if you try to left-shift a smaller integer and store it into a larger one.
By the way, which of the two is most right and portable: (type)var <<
1 or ((type)var) << 1?
Both are fine and portable. Though I prefer the first one since it's shorter. Casting has higher precedence than shift.
Conversion does happen. The problem is the result of the expression anUInt << 2 is an unsigned int because anUInt is an unsigned int.
Casting anUInt to a long long (actually, this is conversion in this particular case) is the correct thing to do.
Neither (type)var << 1 or ((type)var) << 1 is more correct or portable because operator precedence is strictly defined by the Standard. However, the latter is probably better because it's easier to understand to humans looking at the code casually. Others may disagree with this assertion.
EDIT:
Note that in your first example:
unsigned char anUChar = 0xc0;
int anInt = anUChar << 2;
...the result of the expression anUChar << 2 is not an unsigned char as you might expect, but an int because of Integral Promotion.
The operands of operator<< are integral or enumeration type (See Standard 5.8/1). When a binary operator that expects operands of arithmetic or enumeration type is called, the compiler attempts to convert both operands to the same type, so that the expression may yield a common type. In this case, integral promotion is performed on both operands (5/9). When an unsigned char takes part in integral promotion, it will be converted to an int if your platform can accomodate all possible values of unsigned char in an int, else it will be converted to an unsigned int (4.5/1).
Shorter integral types are promoted to an int type for bitshift operations. This has nothing to do with the type to which you assign the result of the shift.
On 64-bit machines, your second piece of code would be equally problematic since the int types are usually also 32 bit wide. (Between x86 and x64, long long int is typically always 64 and int 32 bits, only long int depends on the platform.)
(In the spirit of C++, I would write the conversion as (unsigned long long int)(anUInt) << 2, evocative of the conversion-constructor syntax. The first set of parentheses is purely because the type name consists of several tokens.)
I would also prefer to do bitshifting exclusively on unsigned types, because only unsigned types can be considered equivalent (in terms of values) to their own bit pattern value.
Because of integer promotions. For most operators (e.g. <<), char operands are promoted to int first.
This has nothing to do with where the result of the calculation is going. In other words, the fact that your second example assigns to a long long does not affect the promotion of the operands.

Which variables should I typecast when doing math operations in C/C++?

For example, when I'm dividing two ints and want a float returned, I superstitiously write something like this:
int a = 2, b = 3;
float c = (float)a / (float)b;
If I do not cast a and b to floats, it'll do integer division and return an int.
Similarly, if I want to multiply a signed 8-bit number with an unsigned 8-bit number, I will cast them to signed 16-bit numbers before multiplying for fear of overflow:
u8 a = 255;
s8 b = -127;
s16 = (s16)a * (s16)b;
How exactly does the compiler behave in these situations when not casting at all or when only casting one of the variables? Do I really need to explicitly cast all of the variables, or just the one on the left, or the one on the right?
Question 1: Float division
int a = 2, b = 3;
float c = static_cast<float>(a) / b; // need to convert 1 operand to a float
Question 2: How the compiler works
Five rules of thumb to remember:
Arithmetic operations are always performed on values of the same type.
The result type is the same as the operands (after promotion)
The smallest type arithmetic operations are performed on is int.
ANSCI C (and thus C++) use value preserving integer promotion.
Each operation is done in isolation.
The ANSI C rules are as follows:
Most of these rules also apply to C++ though not all types are officially supported (yet).
If either operand is a long double the other is converted to a long double.
If either operand is a double the other is converted to a double.
If either operand is a float the other is converted to a float.
If either operand is a unsigned long long the other is converted to unsigned long long.
If either operand is a long long the other is converted to long long.
If either operand is a unsigned long the other is converted to unsigned long.
If either operand is a long the other is converted to long.
If either operand is a unsigned int the other is converted to unsigned int.
Otherwise both operands are converted to int.
Overflow
Overflow is always a problem. Note. The type of the result is the same as the input operands so all the operations can overflow, so yes you do need to worry about it (though the language does not provide any explicit way to catch this happening.
As a side note:
Unsigned division can not overflow but signed division can.
std::numeric_limits<int>::max() / -1 // No Overflow
std::numeric_limits<int>::min() / -1 // Will Overflow
In general, if operands are of different types, the compiler will promote all to the largest or most precise type:
If one number is... And the other is... The compiler will promote to...
------------------- ------------------- -------------------------------
char int int
signed unsigned unsigned
char or int float float
float double double
Examples:
char + int ==> int
signed int + unsigned char ==> unsigned int
float + int ==> float
Beware, though, that promotion occurs only as required for each intermediate calculation, so:
4.0 + 5/3 = 4.0 + 1 = 5.0
This is because the integer division is performed first, then the result is promoted to float for the addition.
You can just cast one of them. It doesn't matter which one though.
Whenever the types don't match, the "smaller" type is automatically promoted to the "larger" type, with floating point being "larger" than integer types.
Division of integers: cast any one of the operands, no need to cast them both. If both operands are integers the division operation is an integer division, otherwise it is a floating-point division.
As for the overflow question, there is no need to explicitly cast, as the compiler implicitly does that for you:
#include <iostream>
#include <limits>
using namespace std;
int main()
{
signed int a = numeric_limits<signed int>::max();
unsigned int b = a + 1; // implicit cast, no overflow here
cout << a << ' ' << b << endl;
return 0;
}
In the case of the floating-point division, as long as one variable is of a floating-point datatype (float or double), then the other variable should be widened to a floating-point type, and floating-point division should occur; so there's no need to cast both to a float.
Having said that, I always cast both to a float, anyway.
I think as long as you are casting just one of the two variables the compiler will behave properly (At least on the compilers that I know).
So all of:
float c = (float)a / b;
float c = a / (float)b;
float c = (float)a / (float)b;
will have the same result.
Then there are older brain-damaged types like me who, having to use old-fashioned languages, just unthinkingly write stuff like
int a;
int b;
float z;
z = a*1.0*b;
Of course this isn't universal, good only for pretty much just this case.
Having worked on safety-critical systems, i tend to be paranoid and always cast both factors: float(a)/float(b) - just in case some subtle gotcha is planning to bite me later. No matter how good the compiler is said to be, no matter how well-defined the details are in the official language specs. Paranoia: a programmer's best friend!
Do you need to cast one or two sides? The answer isn't dictated by the compiler. It has to know the exact, precse rules. Instead, the answer should be dictated by the person who will read the code later. For that reason alone, cast both sides to the same type. Implicit truncation might be visible enough, so that cast could be redundant.
e.g. this cast float->int is obvious.
int a = float(foo()) * float(c);