1

I want to save the value of a float variable named f in the third element of an array named i in a way that the floating point part isn't wiped (i.e. I don't want to save 1 instead of 1.5). After that, complete the last line in a way that we see 1.5 in the output (don't use cout<<1.5; or cout<<f; or some similar tricks!)

float f=1.5;
int i[3];
i[2] = ... ;
cout<<... ; 

Does anybody have any idea?

Ebrahim Ghasemi
  • 5,850
  • 10
  • 52
  • 113

3 Answers3

8

Use type-punning with union if they have the same size under a compilation environment:

static_assert(sizeof(int) == sizeof(float));

int castFloatToInt(float f) {
    union { float f; int i; } u;
    u.f = f;
    return u.i;
}

float castIntToFloat(int i) {
    union { float f; int i; } u;
    u.i = i;
    return u.f;
}

// ...

float f=1.5;
int i[3];
i[2] = castFloatToInt(f);
cout << castIntToFloat(i); 

Using union is the way to prevent aliasing problem, otherwise compiler may generate incorrect results due to optimization.

This is a common technique for manipulating bits of float directly. Although normally uint32_t will be used instead.

Milo Yip
  • 4,902
  • 2
  • 25
  • 27
3

Generally speaking, you cannot store a float in an int without loss of precision.
You could multiply your number with a factor, store it and after that divide again to get some decimal places out of it.
Note that this will not work for all numbers and you have to choose your factor carefully.

float f = 1.5f;
const float factor = 10.0f;
int i[3];
i[2] = static_cast<int>(f * factor);
std::cout << static_cast<float>(i[2]) / factor;
Beta Carotin
  • 1,659
  • 1
  • 10
  • 27
2

If we can assume that int is 32 bits then you can do it with type-punning:

float f = 1.5;
int i[3];
i[2] = *(int *)&f;
cout << *(float *)&i[2];

but this is getting into Undefined Behaviour territory (breaking aliasing rules), since it accesses a type via a pointer to a different (incompatible) type.

LIVE DEMO

Paul R
  • 208,748
  • 37
  • 389
  • 560
  • 1
    No, because `int` is not guaranteed to be 32 bits long. – Beta Carotin Apr 25 '15 at 15:12
  • 1
    @BetaCarotin: OK - qualified the answer now - I'd already pointed out that this is UB, but I expect it's a terrible interview question and this is the kind of answer they might be looking for (unfortunately this is all too common). – Paul R Apr 25 '15 at 15:13
  • Practically speaking, this is more implementation defined than UB, evil but useful, like this one, http://en.wikipedia.org/wiki/Fast_inverse_square_root – user3528438 Apr 25 '15 at 15:48
  • @user3528438: I think the type-punning is UB in C++, strictly speaking, but because it's common in old legacy code most compilers allow you to disable assumptions about aliasing (e.g. `g++ -fno-strict-aliasing`), so you can ensure that it works, even though it breaks the rules. It's ugly though. – Paul R Apr 25 '15 at 15:52
  • @PaulR May I ask you to please add a brief explanation also? – Ebrahim Ghasemi Apr 25 '15 at 15:52
  • @Abraham: OK - I've added a little clarification. Let me know if anything is still not clear. – Paul R Apr 25 '15 at 15:54
  • @PaulR Thank you. Just to be sure that I got the point : Is this right: `&f` is the address of a `float` number. `(int*)&f` act like a pointer to `int` and so the value of `*(int*)&f` is the value of a pointer to `int`, while it is pointing to `float` in real. right? And for the last line again we have : `&i[2]` is the address of an `int` number. `(float*)&i[2]` act like a pointer to `float`, and it point to `float` number in real also. right again? – Ebrahim Ghasemi Apr 25 '15 at 16:09
  • @Abraham: yes, you've pretty much got it. – Paul R Apr 25 '15 at 18:49