I'm an IT student and today I had a C programming exam.
One of questions is a bit confusing: Is it possible to cast float* to int* ?
I said no whats your opinion?
You can cast, but you won't magically get an integer version of the float.
What happens is that you have a float somewhere in memory and then have an integer point to the same location. But the two are represented differently (e.g., two's complement vs IEEE 754), and you will get a vastly different integer as a result.
For example:
#include <stdio.h>
int main() {
printf("sizeof(int) = %zu\n", sizeof(int));
printf("sizeof(float) = %zu\n", sizeof(float));
float m = 456.78f;
int *p = (int*)&m;
printf("Float: %f\n", m);
printf("Integer: %d\n", *p);
return 0;
}
On my system, this outputs:
sizeof(int) = 4
sizeof(float) = 4
Float: 456.779999
Integer: 1139041239
I print their sizes as well, to emphasize that they could be different as well.
E.g., if the integer happened to be larger, you would then touch memory outside of the float.
While this is probably not what you want, there are uses for it. For example, if you want to see the bit-representation for a specific float, you could cast it to something like uint8_t and inspect it up to sizeof(float).
Why not just try it out?
$ cat foo.c
#include <stdio.h>
int main(){
float f = 0.15625;
float *p = &f;
printf("%f\n", *p);
printf("%i\n", *((int *)p));
}
$ gcc foo.c
$ ./a.out
0.156250
1042284544
The float is 32-bits and you can read about how its represented: http://en.wikipedia.org/wiki/Single-precision_floating-point_format
Casting the float pointer to an int pointer is just telling C that "the data I'm pointing to is an int".
The float 0.156250 is represented by the binary 0111110001000000000000000000000, which represents the integer 1042284544.
EDIT: #csl beat me to the answer :)
Related
I am not able to understand how the following code is working:
#include<stdio.h>
int main() {
int i = 100;
int *a = &i;
float *f = (float *)a;
(*f)++;
printf("%d", *a); //getting some garbage value
}
'f' is pointing to the same memory location as that of 'a'. So, (*f)++ should in turn increment the value of i to 101. Where am I going wrong?
Floats and ints are stored with different binary representations. When you cast a float to an int or vice versa, the compiler takes care of this for you. But in your case, you're casting an int* to a float*, so you're modifying a float that has the wrong value, since the binary representation didn't get converted.
Having a little difficulty with pointers. I have to store a float in an array of unsigned ints and be able to pull it out.
I know there is a special way to cast this so I don't reorder the bits, I think this is the correct way to store it when I want to put it into the array:
float f = 5.0;
int newF = (int *) f;
arrayOfInts[0] = *newF
Which seems to successfully store the value in the array.
However, at some point I have to pull the value back out of the array of ints, this is where my confusion comes in (assuming I inputed into the array correctly)
float * f = (float *) arrayOfInts[0]
int result = *f;
however, that gives me the warning: 'cast to pointer from integer of different size'
I can't really think of how to solve that without some sort of long cast.. which doesn't seem right..
I don't want to lose the value or damage the bits.. obviously It will lose decimal point precision.. but I know theirs some way to safety convert back and forth
I have to store a float in an array of unsigned ints and be able to pull it out.
Use a union and unsigned char[]. unsigned char is specified to not have any padding and all bit combinations are valid. This is not always true of many other number types. By overlaying the float with unsigned char[], code can examine each "byte" of the float, one at a time.
union {
float f;
unsigned char uc[sizeof (float)];
} x;
// example usage
x.f = 1.234f;
for (unsigned i = 0; i<sizeof x.uc; i++) {
printf("%u:%u\n", i, 1u*x.uc[i]);
}
Sample output: Yours may vary
0:182
1:243
2:157
3:63
float --> unsigned char[] --> float is always safe.
unsigned char[] --> float --> unsigned char[] is not always safe as a combination of unsigned char[] may not have a valid float value.
Avoid pointer tricks and casting. There are alignment and size issues.
// Poor code
float f = 5.0f;
int newF = *((int *) &f); // Conversion of `float*` to `int*` is not well specified.
Code can also overlay with fixed-width no-padding types like (u)int32_t if they exist (they usually do) and match in size.
#include <stdint.h>
union {
float f;
uint32_t u32;
} x32;
#include <assert.h>
#include <inttypes.h>
// example usage
assert(sizeof x32.f == sizeof x32.u32);
x32.f = 1.234f;
printf("%" PRNu32 "\n", x32.u32);
}
Example output: yours may vary
1067316150
To convert a float to an int
float fval = 123.4f;
int ival = *(int*)&fval;
To convert back
int ival = /* from float */
float fval = *(float*) &ival;
it won't work if float and int are different sizes, but presumably you know that already. The unsigned char union method outlined in other answer for chux is more robust, but over-complicated for what you probably want to do.
#include <stdio.h>
int main () {
char c = 'A';
int *int_ptr;
double *double_ptr;
*int_ptr = *(int *)&c;
*double_ptr = *(double *)&c;
printf("Original char = %c \n", c);
printf("Integer pointer = %d \n", *int_ptr);
printf("Double pointer = %f\n", *double_ptr);
return 0;
}
The questing is – Why can't I assign the double_ptr using this code, because it causes segmentation fault, but works fine for integer?
As I understand char is 1-byte long and int is 4-bytes long, so double is 8 bytes-long.
By using expression *(double *)&c I expect the following:
& – Get the memory address of c.
(double *) – pretend that this is a pointer to double.
*() – get the actual value and assign it to double var.
Your code has Undefined Behaviour. Therefore anything could happen.
The UB is because you are casting a char which is one byte to types that are 4 and 8 bytes, which means you are (potentially) accessing memory out of bounds, or with the wrong alignment.
Whether any of this will "work" or "not work" on any particular system is not very relevant, because the code is erroneous.
In your program, typecast of char to int* or double* and then a dereference would get some number of extra bytes from memory, which is undefined behavior.
It is possible to access to the bit representation of a floating point constant in C;
For example i'd like to assign
uint64_t x = //bit representation of 5.74;
which is represented by
0x40b7ae14
Do you think it is possible?
One way of achieving this is to use a union:
union {
double fltValue;
uint64_t uintValue;
} conversion;
conversion.fltValue = 5.74;
printf("%#llx\n", conversion.uintValue);
Updated with %#x thanks to Aracthor for mentioning it. And %#llx thanks to EOF.
For a working example (with float instead of double) see:
https://ideone.com/p4rH5l
It is possible, but is not portable because you cannot be sure of how a floating point value is represented : C standard does not define it.
You could simply use casting of pointers :
float x = 5.74;
void *pt = &x
uint64_t *ip = pt;
uint64_t i = *ip;
This is formal undefined behaviour because you are casting a pointer to a different type, and you should not do it because you add endian problems to the floating point representation.
The correct way would be :
float x = 5.74;
void *pt = &x
unsigned char *ip = pt;
ip now point to a unsigned char[] of size sizeof(float) containing the binary representation of a float. And no undefined behaviour was invoked because casting a pointer to a void *or a char * is always allowed.
#include <stdint.h>
#include <stdio.h>
int main(void) {
double x = 5.74;
uint64_t y = *((uint64_t*)&x);
printf("0x%016llx", y); /* 0x4016f5c28f5c28f6 */
}
If you are asking for the binary representation of float, then take an integer pointer..assign the address of the float variable to the integer pointer and by derefenencing the value of int pointer, get its binary form..and in the middle if you somehow want to avoid the warning generated by unusual typecasting then you can always use void*
When subtracting two pointers from each other the difference represents the number of elements between them (when pointing to int).
Why is the difference zero when pointing to type double?
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
double data[10] = {1,2,3,4,5,6,7,8,9};
double *iptr1;
double *iptr2;
double val;
iptr1 = &data[0];
iptr2 = &data[9];
val = iptr2 - iptr1;
printf("Distance between the two addresses = %d\n", val);
return 0;
}
You are printing a double value with %d, which expects an integer. Change the type of val to int and everything will work as expected.
Note that, as suggested in the comments below, C defines a type for pointer differences, ptrdiff_t. This is guaranteed to always hold a value arising from a pointer difference, regardless of how far the actual pointers are. Although, if you are absolutely sure that the difference will fit an int, as in your question, that type should be fine as well. But double makes no sense at all, pointer differences cannot be fractional values.
Double is not the correct type for pointer differences. Use ptrdiff_t, as defined in stddef.h instead.
See here for the Data Types
Instead of
printf("Distance between the two addresses = %d\n", val);
use then also
ptrdiff_t val = iptr2 - iptr1;
printf("Distance between the two addresses = %td\n", val);