double vs float... can somebody explain this code to me?

Why is the output of this piece of code

1
2
12
nan


If I replace double with float, it works as expected, the output is correct. Thank you!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
using namespace std;

void  foo(char* test, int i, double d) {

	int* pi = (int*) test;
	*pi = i;
	pi++;

	double* dp = (double*) pi;
	*dp = d;
	dp++;

}

int main() {

	struct s {
		int i;
		double d;
	};

	s t;

	foo((char*) &t, 12, 2.123645);

	cout << t.i << endl;
	cout << t.d << endl;

	return 0;
}

Probably data inaccuracy. Just stick to double because float is too small for real world purposes.
double* dp = (double*) pi;

This is highly dangerous.

You are taking an int* (which points to lets say 4 bytes) and casting it to a double* (which points to some that is supposed to be more than 4 bytes. This means when you deference your double*, you are reading random data (which might not even be yours) and it will probably be garbage, hence the NaN.
1
2
3
4
5
6
7
8
9
10
11
void  foo(char* test, int i, double d) {

	int* pi = (int*) test;
	*pi = i;
	pi++;

	double* dp = (double*) pi;
	*dp = d;
	dp++;

}


!!!!

Why not just pass an s* pointer to this function? Why all the typeless casting and raw pointer manipulation.

Ew ew ew.

You'd be lucky if this works at all. The compiler will have to arrange memory in a very specific way in order for this to work (read: it doesn't have to, so this might not always work even if it's working for you now)


EDIT:

After further thought, what's happening in your original code might be what I speculated. The compiler isn't arranging memory like you think it should. It might be putting some bytes of padding between 'i' and 'd' members of the s struct.
Last edited on
It happens to work on float just because the sizes of a float variable and an int variable are the same (4 bytes). If you use GCC, in the "double" case, the size of struct s is 16 bytes (i.e., 2*size(double)), instead of 4+8 bytes. So, after pi++, pi is not pointed to the correct address of the double variable in t.

To play with this code, you can try:
1
2
3
4
5
6
7
void foo(char* test, int i, double d) {
  int* pi = (int*) test;
  *pi = i;
  double* dp = (double*) pi;
  dp ++;
  *dp = d;
}


However, this coding style is very easy to make mistake. Pass a pointer of struct s is a better choice.
Thanks for your answers guys. I should have clarified that this is a question from a test. So it's bad programming, but it's intentional to camouflage what is really going on. Btw, on some compilers/platforms it prints NaN, on others a random number.

I think Disch may be on the right track:
After further thought, what's happening in your original code might be what I speculated. The compiler isn't arranging memory like you think it should. It might be putting some bytes of padding between 'i' and 'd' members of the s struct.


Does anybody have any more insights into that? Why does it work for the int and when I replace the double with a float? And why can I access the double number in foo() fine, but not in main() anymore?
Last edited on
Thanks webbertiger,

I think that's it:
If you use GCC, in the "double" case, the size of struct s is 16 bytes (i.e., 2*size(double)), instead of 4+8 bytes. So, after pi++, pi is not pointed to the correct address of the double variable in t.


But can you please explain me why the size of the struct is 16 bytes, and not 12?

This link tells the reason of C++ structure using a bigger size than the members actually need:
http://www.codeguru.com/forum/showthread.php?t=276622
Thanks webbertiger, I got it now.
Topic archived. No new replies allowed.