I have this simple C program.
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
bool foo (unsigned int a) {
return (a > -2L);
}
bool bar (unsigned long a) {
return (a > -2L);
}
int main() {
printf("foo returned = %d\n", foo(99));
printf("bar returned = %d\n", bar(99));
return 0;
}
Output when I run this -
foo returned = 1
bar returned = 0
Recreated in godbolt here
My question is why does foo(99) return true but bar(99) return false.
To me it makes sense that bar would return false. For simplicity lets say longs are 8 bits, then (using twos complement for signed value):
99 == 0110 0011
-2 == unsigned 254 == 1111 1110
So clearly the CMP instruction will see that 1111 1110 is bigger and return false.
But I dont understand what is going on behind the scenes in the foo function. The assembly for foo seems to hardcode to always return mov eax,0x1. I would have expected foo to do something similar to bar. What is going on here?
This is covered in C classes and is specified in the documentation. Here is how you use documents to figure this out.
In the 2018 C standard, you can look up > or “relational expressions” in the index to see they are discussed on pages 68-69. On page 68, you will find clause 6.5.8, which covers relational operators, including >. Reading it, paragraph 3 says:
If both of the operands have arithmetic type, the usual arithmetic conversions are performed.
“Usual arithmetic conversions” is listed in the index as defined on page 39. Page 39 has clause 6.3.1.8, “Usual arithmetic conversions.” This clause explains that operands of arithmetic types are converted to a common type, and it gives rules determining the common type. For two integer types of different signedness, such as the unsigned long and the long int in bar (a and -2L), it says that, if the unsigned type has rank greater than or equal to the rank of the other type, the signed type is converted to the unsigned type.
“Rank” is not in the index, but you can search the document to find it is discussed in clause 6.3.1.1, where it tells you the rank of long int is greater than the rank of int, and the any unsigned type has the same rank as the corresponding type.
Now you can consider a > -2L in bar, where a is unsigned long. Here we have an unsigned long compared with a long. They have the same rank, so -2L is converted to unsigned long. Conversion of a signed integer to unsigned is discussed in clause 6.3.1.3. It says the value is converted by wrapping it modulo ULONG_MAX+1, so converting the signed long −2 produces a ULONG_MAX+1−2 = ULONG_MAX−1, which is a large integer. Then comparing a, which has the value 99, to a large integer with > yields false, so zero is returned.
For foo, we continue with the rules for the usual arithmetic conversions. When the unsigned type does not have rank greater than or equal to the rank of the signed type, but the signed type can represent all the values of the type of the operand with unsigned type, the operand with the unsigned type is converted to the operand of the signed type. In foo, a is unsigned int and -2L is long int. Presumably in your C implementation, long int is 64 bits, so it can represent all the values of a 32-bit unsigned int. So this rule applies, and a is converted to long int. This does not change the value. So the original value of a, 99, is compared to −2 with >, and this yields true, so one is returned.
In the first function
bool foo (unsigned int a) {
return (a > -2L);
}
the both operands of the expression a > -2L have the type long (the first operand is converted to the type long due to the usual arithmetic conversions because the rank of the type long is greater than the rank of the type unsigned int and all values of the type unsigned int in the used system can be represented by the type long). And it is evident that the positive value 99L is greater than the negative value -2L.
The first function could produce the result 0 provided that sizeof( long ) is equal to sizeof( unsigned int ). In this case the type long is unable to represent all (positive) values of the type unsigned int. As a result due to the usual arithmetic conversions the both operands will be converted to the type unsigned long.
For example running the function foo using MS VS 2019 where sizeof( long ) is equal to 4 as sizeof( unsigned int ) you will get the result 0.
Here is a demonstration program written in C++ that visually shows the reason why the result of a call of the function foo using MS VS 2019 can be equal to 0.
#include <iostream>
#include <iomanip>
#include <type_traits>
int main()
{
unsigned int x = 0;
long y = 0;
std::cout << "sizeof( unsigned int ) = " << sizeof( unsigned int ) << '\n';
std::cout << "sizeof( long ) = " << sizeof(long) << '\n';
std::cout << "std::is_same_v<decltype( x + y ), unsigned long> is "
<< std::boolalpha
<< std::is_same_v<decltype( x + y ), unsigned long>
<< '\n';
}
The program output is
sizeof( unsigned int ) = 4
sizeof( long ) = 4
std::is_same_v<decltype( x + y ), unsigned long> is true
That is in general the result of the first function is implementation defined.
In the second functions
bool bar (unsigned long a) {
return (a > -2L);
}
the both operands have the type unsigned long (again due to the usual arithmetic conversions and ranks of the types unsigned long and signed long are equal each other, so an object of the type signed long is converted to the type unsigned long) and -2L interpreted as unsigned long is greater than 99.
The reason for this has to do with the rules of integer conversions.
In the first case, you compare an unsigned int with a long using the > operator, and in the second case you compare a unsigned long with a long.
These operands must first be converted to a common type using the usual arithmetic conversions. These are spelled out in section 6.3.1.8p1 of the C standard, with the following excerpt focusing on integer conversions:
If both operands have the same type, then no further conversion is
needed.
Otherwise, if both operands have signed integer types or both have
unsigned integer types, the operand with the type of lesser integer
conversion rank is converted to the type of the operand with greater
rank.
Otherwise, if the operand that has unsigned integer type has rank
greater or equal to the rank of the type of the other operand, then
the operand with signed integer type is converted to the type of the
operand with unsigned integer type.
Otherwise, if the type of the operand with signed integer type can
represent all of the values of the type of the operand with unsigned
integer type, then the operand with unsigned integer type is converted
to the type of the operand with signed integer type.
Otherwise, both operands are converted to the unsigned integer type
corresponding to the type of the operand with signed integer type.
In the case of comparing an unsigned int with a long the second bolded paragraph applies. long has higher rank and (assuming long is 64 bit and int is 32 bit) can hold all values than an unsigned int can, so the unsigned int operand a is converted to a long. Since the value in question is in the range of long, section 6.3.1.3p1 dictates how the conversion happens:
When a value with integer type is converted to another integer type
other than _Bool, if the value can be represented by the new type, it
is unchanged
So the value is preserved and we're left with 99 > -2 which is true.
In the case of comparing an unsigned long with a long, the first bolded paragraph applies. Both types are of the same rank with different signs, so the long constant -2L is converted to unsigned long. -2 is outside the range of an unsigned long so a value conversion must happen. This conversion is specified in section 6.3.1.3p2:
Otherwise, if the new type is unsigned, the value is converted by
repeatedly adding or subtracting one more than the maximum value that
can be represented in the new type until the value is in the range of
the new type.
So the long value -2 will be converted to the unsigned long value 264-2, assuming unsigned long is 64 bit. So we're left with 99 > 264-2, which is false.
I think what is happening here is implicit promotion by the compiler. When you perform comparison on two different primitives, the compiler will promote one of them to the same type as the other. I believe the rules are that the type with the larger possible value is used as the standard.
So in foo() you are implicitly promoting your argument to a signed long type and the comparison works as expected.
In bar() your argument is an unsigned long, which has a larger maximum value than signed long. Here the compiler promotes -2L to unsigned long, which turns into a very large number.
I am not familiar with the problems about memory alignment and pointer conversion. I am learning from the Nvidia official sample code as the following.
half *A = NULL;
half *B = NULL;
float *C = NULL;
float *D = NULL;
checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&A),
sizeof(half) * M_GLOBAL * K_GLOBAL));
checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&B),
sizeof(half) * N_GLOBAL * K_GLOBAL));
checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&C),
sizeof(float) * M_GLOBAL * N_GLOBAL));
checkCudaErrors(cudaMalloc(reinterpret_cast<void **>(&D),
sizeof(float) * M_GLOBAL * N_GLOBAL));
assert(((unsigned long long)A) % 128 == 0);
assert(((unsigned long long)B) % 128 == 0);
assert(((unsigned long long)C) % 128 == 0);
assert(((unsigned long long)D) % 128 == 0);
I have three questions toward to this.
I know the difference between pointer type conversion and numerical type conversion. With respect to pointer type conversion, we just change the way to understand that pointer. But how should I understand the conversion from half* to unsigned long long*?
I think the memory address should keep unchanged during this conversion. Why do we need to convert to unsigned long long* first and then check if the memory is aligned with 128?
But how should I understand the conversion from half* to unsigned long long*?
There is no conversion to unsigned long long * in the code you show. There is a conversion to unsigned long long.
The purpose of the conversion is to convert the address stored in one of A, B, C, or D to an integer so that its bits may be examined. The C standard does not define the result of converting a pointer to an integer type except for some basic properties, but the conversion is “intended to be consistent with the addressing structure of the execution environment” (C 2018 footnote 69). In the compiler Nvidia uses, the conversion presumably produces the address as normally used by the processor architecture. Then using % 128 == 0 tests whether the address is aligned to a multiple of 128 bytes.
Why do we need to convert to unsigned long long* first and then check if the memory is aligned with 128?
The % operator will not accept a pointer operand, so the operand must be converted to an integer type, unsigned long long, not unsigned long long *.
I implemented this function to convert from dword_ptr to dword but if there is other way with less code that will be appreciated.
this code should be compatible with 32bit and 64bit windows
I have seen this Question but the answer contains new and delete operations
inline unsigned long Convert_to_DWORD(unsigned long long data)
{
//typedef unsigned __int64 ULONG_PTR
//typedef ULONG_PTR DWORD_PTR
//typedef unsigned long DWORD;
assert(UINT_MAX > data);
unsigned long* value = reinterpret_cast<unsigned long*>(&data);
return *value;
}
Is there a safe way to cast DWORD_PTR into DWORD?
Integer types are implicitly convertible to each other. Such conversion is safe in the sense that there is no UB involved
DWORD_PTR original;
DWORD converted = original;
On a 64 bit system DWORD may be smaller than DWORD_PTR, in which case it cannot represent all the values representable by DWORD_PTR. Therefore if you do the inverse conversion, you do not get the same original value back unless the high order bytes happened to be zero. Furthermore, if DWORD_PTR represented a pointer value, then converting the value to DWORD then back to DWORD_PTR then back into a pointer then using the resulting pointer would not be safe.
Since the conversion is narrowing, a compiler might generate a warning. An explicit conversion makes your intention unambiguous and should silence such warning:
DWORD converted = DWORD(original);
int left = std::numeric_limits<int>::min();
int right = -1;
//the code below instead of give one more than int_max gives: 18446744071562067968
unsigned long long result = left * right;
I've tried to look up UAC but even according to UAC rules this should produce correct output. Any ideas why the result is incorrect?
It's undefined behavior to multiply the minimum value of a signed 2's complement int by -1, because the result is outside the range of the type.
In this case your output is consistent with the result having been -2147483648, i.e. the overflow appears to have wrapped around. You cannot rely on wraparound for signed types, only unsigned types.
Assigning the result of a calculation to unsigned long long does not change what type the calculation is performed in. As soon as you do the multiplication, you lose. So, convert one of the operands to unsigned long long before multiplying.
Both operands are int, so arithmetic is performed within the int type; the result of the operation overflows the range of int, so the result is undefined.
To get the result you expect, cast one operand to long long first:
unsigned long long result = left * (long long) right;
This is still potentially undefined behaviour; it's safer to convert to unsigned arithmetic as early as possible (since unsigned arithmetic wraps and doesn't overflow):
unsigned long long result = left * (unsigned long long) right;
Note that the result you arrived at is 0xffffffff80000000; this indicates that the actual result of the operation was std::numeric_limits<int>::min() in the int type, which was then sign-extended and cast to unsigned long long.
Tha cause is that the multiplication is commited in terms of int.
Both arguments are int, so multiplications gives an int agein, and you were right, it gives int_max + 1 which is equivivalent to int_min=-2147483648. So it is actually -2147483648, but for unsigned long long it is equivivalent to 18446744071562067968, see hex codes:
int_min = 80000000
(unsigned long long) (int min) = ffffffff80000000
why d is not equal b in this example?
unsigned int z = 176400;
long a = -4;
long b = a*z/1000; //b=4294261
long c = a*z; // c=-705600
long d = c/1000; // d =-705
I use Visual Studio 2008, windows XP, core2duo.
Thanks.
It looks like you are using a platform where int and long have the same size. (I've inferred this by the fact that if long was able to hold all the valid values of unsigned int you would not see the behaviour that you are seeing.)
This means that in the expression a*z, both a and z are converted to unsigned long and the result has type unsigned long. (ISO/IEC 14882:2011, 5 [expr] / 9 ... "Otherwise, both operands shall be converted to the unsigned integer type corresponding to the type of the operand with signed integer type.")
c is the result of converting this expression from unsigned long to long and in your case this results in an implementation defined result (that happens to be negative) as the positive value of a*z is not representable in a signed long. In c/1000, 1000 is converted to long and long division is performed (no pun intended) resulting in a long (which happens to be negative) and is stored to d.
In the expressions a*z/1000, 1000 (an expression of type int) is converted to unsigned long and the division is performed between two unsigned long resulting in a positive result. This result is representable as a long and the value is unchanged on converting to long and storing to b.