C++ : int* / float* to char*, why get different result using reinterpret_cast? - c++

int* to char* :
int* pNum = new int[1];
pNum[0] = 57;
char* pChar = reinterpret_cast< char* >(pNum);
Result : pChar[0] = '9'; //'9' ASCII 57
float* to char* :
float* pFloat = new float[1];
pFloat[0] = 57; //assign the same value as before
char* pChar = reinterpret_cast< char* >(pFloat);
Result : pChar[0] = 'a';
So why I'm getting two different results ?
Thanks for your help.

You have this because floating point values don't use the same encoding as integer values (IEEE encoding with mantissa+exponent or something like that)
Besides, I suppose you're running a little endian CPU, otherwise your first test would have yielded 0 (I mean '\0').

Both float and int are data types which are (usually) represented by four bytes:
b1 b2 b3 b4
However, those bytes are interpreted quite differently across the two types - if they wouldn't, there would be hardly any need for two types.
Now if you reinterpret the pointers to pointers-to-char, the result points only to the first byte, as this is the length of a char:
b1 b2 b3 b4
^^
your char* points to here
As said, this first byte has a very different meaning for the two data types, and this is why the representation as a char in general differs.
Application to your example:
The number 57 in float (IEEE754 Single precision 32-bit) is represented in bits as
01000010 01100100 00000000 00000000
In contrast, the representation in a 32-bit integer format is
00000000 00000000 00000000 00111001
Here the number seems to be represented in "big-endian" format, where the most important byte (the one which changes the value of the int the most) comes first. As mentioned by #Jean-FrançoisFabre, in your PC it seems to be the other way round, but nevermind. For both conversions, I used this site.
Now your char* pointers point to the first of those 8-bit-blocks, respectively. And obviously they're different.

Related

SIMD __m256i to __m256d cast results

I am trying to cast a SIMD integer variable into a double. But I can't see what the result of this operation will be.
Example:
int arr[8]={12345678,12333333,12344444,12355555,12366666,12377777,12388888,12399999};
__m256i temp = _mm256_load_si256((__m256i *) arr);
__m256d temp2 = _mm256_castsi256_pd (temp);
as a result of this operation what are the members in my temp2?
Short answer
The members in temp2 will be:
{4.014635e-305, 4.062922e-305, 4.111209e-305, 4.159495e-305}
How to obtain the values
Just write the SIMD data into an double array back and print it.
#include <stdio.h>
#include <immintrin.h>
int main(void) {
int hoge[4]; /* hack that worked on tested environment to avoid Segmentation Fault */
double result[4];
int i;
int arr[8]={12345678,12333333,12344444,12355555,12366666,12377777,12388888,12399999};
__m256i temp = _mm256_load_si256((__m256i *) arr);
__m256d temp2 = _mm256_castsi256_pd (temp);
_mm256_storeu_pd(result, temp2);
for (i = 0; i < 4; i++) printf("result[%d] = %.6e (%.15a)\n", i, result[i], result[i]);
return 0;
}
I ran this code on Wandbox and got this output:
result[0] = 4.014635e-305 (0x1.c311500bc614e00p-1012)
result[1] = 4.062922e-305 (0x1.c87e300bc5c7c00p-1012)
result[2] = 4.111209e-305 (0x1.cdeb100bcb34a00p-1012)
result[3] = 4.159495e-305 (0x1.d357f00bd0a1800p-1012)
You can write the SIMD data to an double array via _mm256_storeu_pd().
An exception may be generated when address that is not 32-byte aligned is passed into _mm256_load_si256(), so you should do the alignment. Actually Segmentation Fault occurred on Wandbox, so I inserted the dummy array hoge to do the alignment.
Why the values were obtained
_mm256_castsi256_pd() is actually just copying the bytes and changing their interpretation.
Assuming little-endian is used and int is 4-byte long,
the data in arr is like this in the byte-addressed memory:
data in arr[8]:
| 12345678| 12333333| 12344444| 12355555| 12366666| 12377777| 12388888| 12399999|
byte data in arr[8] (in little endian):
|4e 61 bc 00|15 31 bc 00|7c 5c bc 00|e3 87 bc 00|4a b3 bc 00|b1 de bc 00|18 0a bd 00|7f 35 bd 00|
data seen as 64-bit hex:
| 0x00bc311500bc614e| 0x00bc87e300bc5c7c| 0x00bcdeb100bcb34a| 0x00bd357f00bd0a18|
Then, assuming that 64-bit IEEE754 is used in double, the 64-bit data consists of 1-bit sign, 11-bit exponent and 52-bit significand.
Taking the first element 0x00bc311500bc614e as example, the sign bit is 0 (plus/zero), the exponent is 0x00b (11 - 1023 = -1012) and the significand is 0xc311500bc614e.
This matches with what is printed via %.15a in the sample code above.
(two extra 0s are printed because printing 15 digits is specified while only data for 13 digits is reordered, so the remainder is padded with 0.)
The other elements also matches like this.
Operation _mm256_castsi256_pd does literally nothing, it is a reinterpretation - equivalent to:
int v_i;
double d_i = *((double*)(int*)&v_i).
Use __m256d _mm256_cvtepi32_pd (__m128i a) as it actually converts 4 integers to 4 doubles.
alignas(16) int arr[4]={12345678,12333333,12344444,12355555};
__m128i temp = _mm_load_si128((__m128i *) arr);
__m256d temp2 = _mm256_cvtepi32_pd(temp);
Note: the loading operations _mm_load_si128 and _mm256_load_si256 require the addresses to be properly aligned. Else use the unaligned versions _mm_loadu_si128 and _mm256_loadu_si256; thought the unaligned versions are slower.
As a result of this operation, temp2 will contain garbage. For example, the first double lane will be 4.0146351468550722e-305.
This is by design. _mm256_castsi256_pd intrinsic doesn’t convert values, it only re-interprets bits in register as doubles.
If you want these double constants in the register, just use _mm256_setr_pd intrinsic:
// Set double values to the constants
__m256d temp2 = _mm256_setr_pd( 12345678, 12333333, 12344444, 12355555 );
Or if these values aren’t constant, use _mm256_cvtepi32_pd intrinsic, here's a complete example:
alignas( 32 ) int arr[ 8 ] = { 12345678, 12333333, 12344444, 12355555,
12366666, 12377777, 12388888, 12399999 };
__m256i integers = _mm256_load_si256( ( const __m256i* ) &arr );
// Convert first 4 int32 values to doubles
__m256d lowDoubles = _mm256_cvtepi32_pd( _mm256_castsi256_si128( integers ) );
// Convert last 4 values to doubles
__m256d highDoubles = _mm256_cvtepi32_pd( _mm256_extracti128_si256( integers, 1 ) );
This will actually convert, not bit cast, the values.
AVX registers hold 256 bits of data. This is 8 int32 values in __m256i type, 8 float values in __m256 data type, but only 4 double values in __m256d type.
P.S. There’s also alignment bug in your code, best way to fix is add alignas(32) before int arr[8]

Casting pointer to other pointer

Right now I'm watching this lecture:
https://www.youtube.com/watch?v=jTSvthW34GU
In around 50th minute of the film he says that this code will return non-zero value:
float f = 7.0;
short s = *(short*)&f;
Correct me if I'm mistaking:
&f is a pointer to float.
We take &f and cast it to pointer to short.
Then we dereference (don't know if it's a verb) that pointer so eventually the whole statement represents a value of 7.
If I print that it displays 0. Why?
Dereferencing through a cast pointer does not cause a conversion to take place the way casting a value does. No bits are changed. So, while
float f = 7.0;
short s = (short)f;
will result in s having the integer value 7,
short s = *(short *)&f;
will simply copy the first 16 bits (depending on platform) of the floating point representation of the value 7.0 into the short. On my system, using little-endian IEEE-754, those bits are all zero, so the value is zero.
Floats are represented internally as 4byte floating point numbers (1 signal bit, 8 exponent bits, 23 mantissa bits) while shorts are 2byte integer types (two's compliment numbers). The code above will reinterpret the top two or bottom two bytes (depending on endianness) of the floating point number as an short integer.
So in the case of 7.0, the floating point number looks like:
0_1000000 1_1100000 00000000 00000000
So on some machines, it will take the bottom 2bytes (all 0s) and on others, it will take the top bytes (non-zero).
For more, see:
Floating-point: http://en.wikipedia.org/wiki/Floating_point
Endianness: http://en.wikipedia.org/wiki/Endianness
Casting a pointer to a different type does not cause any conversion of the pointed-to value; you are just interpreting the pointed-to bytes through the "lens" of a different type.
In the general case, casting a pointer to a different pointer type causes undefined behavior. In this case that behavior happens to depend on your architecture.
To get a picture of what is going on, we can write a general function that will display the bits of an object given a pointer to it:
template <typename T>
void display_bits(T const * p)
{
char const * c = reinterpret_cast<char const *>(p);
for (int i = 0; i < sizeof(T); ++i) {
unsigned char b = static_cast<unsigned char>(*(c++));
for (int j = 0; j < 8; ++j) {
std::cout << ((b & 0x80) ? '1' : '0');
b <<= 1;
}
std::cout << ' ';
}
std::cout << std::endl;
}
If we run the following code, this will give you a good idea of what is going on:
int main() {
float f = 7.0;
display_bits(&f);
display_bits(reinterpret_cast<short*>(&f));
return 0;
}
The output on my system is:
00000000 00000000 11100000 01000000
00000000 00000000
The result you get should now be pretty clear, but again it depends on the compiler and/or architecture. For example, using the same representation for float but on a big-endian machine, the result would be quite different because the bytes in the float would be reversed. In that case the short* would be pointing at the bytes 01000000 11100000.

Code to find Endianness-pointer typecasting

I was trying to search for a code to determine the endianness of the system, and this is what I found:
int main()
{
unsigned int i= 1;
char *c = (char *)&i;
if (*c) {
printf("Little Endian\n");
} else {
printf("Big Endian\n");
}
}
Could someone tell me how this code works? More specifically, why is the ampersand needed here in this typecasting :
char *c = (char *)&i;
What is getting stored into the pointer c.. the value i contains or the actual address i is contained in? Also why is this a char for this program?
While dereferencing a character pointer, only one byte is interpreted(Assuming a char variable takes one byte).And in little-endian mode,the least-significant-byte of an integer is stored first.So for a 4-byte integer,say 3,it is stored as
00000011 00000000 00000000 00000000
while for big-endian mode it is stored as:
00000000 00000000 00000000 00000011
So in the first case, the char* interprets the first byte and displays 3 but in the second case it displays 0.
Had you not typecasted it as :
char *c = (char *)&i;
it will show a warning about incompatible pointer type.Had c been an integer pointer, dereferencing it will get an integer value 3 irrespective of the endianness,as all 4 bytes will be interpreted.
NB You need to initialize the variable i to see the whole picture.Else a garbage value is stored in the variable by default.
Warning!! OP,we discussed the difference between little-endian and big-endian,but it's more important to know the difference between little-endian and little-indian.I noticed that you used the latter.Well, the difference is that little-indian can cost you your dream job in Google or a $3 million in venture capital if your interviewer is a Nikesh Arora,Sundar Pichai,Vinod Dham or Vinod Khosla :-)
Let's try to walk through this: (in comments)
int main(void){ /
unsigned int i = 1; // i is an int in memory that can be conceptualized as
// int[0x00 00 00 01]
char *c = *(char *)&i; // We take the address of i and then cast it to a char pointer
// which we then dereference. This cast from int(4 bytes)
// to char(1 byte) results in only keeping the lowest byte by
if(*c){ // Endian-ness.
puts("little!\n"); // This means that on a Little Endian machine, 0x01 will be
} else { // the byte kept, but on a Big Endian machine, 0x00 is kept.
puts("big!\n"); // int[0x00 00 00 (char)[01]] vs int[0x01 00 00 (char)[00]]
}
return 0;
}

right shift for float

I have float var like that
float f = 0b 00000000 11110001 00000000 00000000
I want to take 1st(not 0st) byte to char variable.
I can't do << and >>.
how can i do that?
There is generally little point messing with the binary representation of floating point values. Any you'll try will not be portable. However, generally, these two work:
char c(reinterpret_cast<char*>(&f)[1]);
union {
float f;
char c[sizeof(float)];
} u = { f };
u.c[1];
char bla;
bla = *((char *) &f + 1)
Also remember that with endianness, on little endian systems what you may actually want is byte 2 (assuming you count your byte from 0 to 3). In that case you would change the + 1 with + 2 in the code above.

Can someone explain this "endian-ness" function for me?

Write a program to determine whether a computer is big-endian or little-endian.
bool endianness() {
int i = 1;
char *ptr;
ptr = (char*) &i;
return (*ptr);
}
So I have the above function. I don't really get it. ptr = (char*) &i, which I think means a pointer to a character at address of where i is sitting, so if an int is 4 bytes, say ABCD, are we talking about A or D when you call char* on that? and why?
Would some one please explain this in more detail? Thanks.
So specifically, ptr = (char*) &i; when you cast it to char*, what part of &i do I get?
If you have a little-endian architecture, i will look like this in memory (in hex):
01 00 00 00
^
If you have a big-endian architecture, i will look like this in memory (in hex):
00 00 00 01
^
The cast to char* gives you a pointer to the first byte of the int (to which I have pointed with a ^), so the value pointed to by the char* will be 01 if you are on a little-endian architecture and 00 if you are on a big-endian architecture.
When you return that value, 0 is converted to false and 1 is converted to true. So, if you have a little-endian architecture, this function will return true and if you have a big-endian architecture, it will return false.
If ptr points to byte A or D depends on the endianness of the machine. ptr points to that byte of the integer that is at the lowest address (the other bytes would be at ptr+1,...).
On a big-endian machine the most significant byte of the integer (which is 0x00) will be stored at this lowest address, so the function will return zero.
On a litte-endian machine it is the opposite, the least significant byte of the integer (0x01) will be stored at the lowest address, so the function will return one in this case.
This is using type punning to access an integer as an array of characters. If the machine is big endian, this will be the major byte, and will have a value of zero, but if the machine is little endian, it will be the minor byte, which will have a value of one. (Instead of accessing i as a single integer, the same memory is accessed as an array of four chars).
Whether *((char*)&i) is byte A or byte D gets to the heart of endianness. On a little endian system, the integer 0x41424344 will be laid out in memory as: 0x44 43 42 41 (least significant byte first; in ASCII, this is "DCBA"). On a big endian system, it will be laid out as: 0x41 42 43 44. A pointer to this integer will hold the address of the first byte. Considering the pointer as an integer pointer, and you get the whole integer. Consider the pointer as a char pointer, and you get the first byte, since that's the size of a char.
Assume int is 4 bytes (in C it may not be). This assumption is just to simplify the example...
You can look at each of these 4 bytes individually.
char is a byte, so it's looking at the first byte of a 4 byte buffer.
If the first byte is non 0 then that tells you if the lowest bit is contained in the first byte.
I randomly chose the number 42 to avoid confusion of any special meaning in the value 1.
int num = 42;
if(*(char *)&num == 42)
{
printf("\nLittle-Endian\n");
}
else
{
printf("Big-Endian\n");
}
Breakdown:
int num = 42;
//memory of the 4 bytes is either: (where each byte is 0 to 255)
//1) 0 0 0 42
//2) 42 0 0 0
char*p = &num;/*Cast the int pointer to a char pointer, pointing to the first byte*/
bool firstByteOf4Is42 = *p == 42;/*Checks to make sure the first byte is 1.*/
//Advance to the 2nd byte
++p;
assert(*p == 0);
//Advance to the 3rd byte
++p;
assert(*p == 0);
//Advance to the 4th byte
++p;
bool lastByteOf4Is42 = *p == 42;
assert(firstByteOf4Is42 == !lastByteOf4Is42);
If firstByteOf4Is42 is true you have little-endian. If lastByteOf4Is42 is true then you have big-endian.
Sure, let's take a look:
bool endianness() {
int i = 1; //This is 0x1:
char *ptr;
ptr = (char*) &i; //pointer to 0001
return (*ptr);
}
If the machine is Little endian, then data will be in *ptr will be 0000 0001.
If the machine is Big Endian, then data will be inverted, that is, i will be
i = 0000 0000 0000 0001 0000 0000 0000 0000
So *ptr will hold 0x0
Finally, the return *ptr is equivalent to
if (*ptr = 0x1 ) //little endian
else //big endian