1

Recently I came across a piece of code that uses a way of casting I found very strange. Here are too examples:

inline float asfloat(unsigned int x){
    return *(float *) &x;
}

inline float asfloat(int x){
    return *(float *) &x;
}

Does this way have any advantages? Wouldn't the following "much simpler" code do the same?

inline float asfloat(unsigned int x){
    return (float)x;
}

inline float asfloat(int x){
    return (float)x;
}
GManNickG
  • 494,350
  • 52
  • 494
  • 543
user1494080
  • 2,064
  • 2
  • 17
  • 36

3 Answers3

9

The former performs a bitwise cast, which is evil (almost as evil as I am). It takes the address of the integer and dereferences it as if it were the address of a floating point number. This also violates aliasing rules and is therefore undefined behavior.

The latter casts an integer into a floating point number preserving the information:

(float)42 == 42.0 // approximately, due to floating point precision errors

I wrote an example that demonstrates that the two are not equal.

  • The union is merely implementation defined, while the cast is undefined. – Billy ONeal Jun 14 '13 at 23:28
  • 3.10 [basic.lval]/10: If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined: * the dynamic type of the object ... * an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic data members – Billy ONeal Jun 14 '13 at 23:29
  • I see. But if I really need the behaviour of the first variant (bitwise cast), then the union solution would be an appropriate way (i.e. well-defined and not violating strict-aliasing)? – user1494080 Jun 14 '13 at 23:31
  • @user: Through the union it is merely implementation defined. (Not well defined because the bit pattern of floats and its are not well defined) – Billy ONeal Jun 14 '13 at 23:33
  • @user1494080: `memcpy` into an intermediate buffer. Note the result is still always strictly implementation-defined, because the bit-representation of `float` and `(unsigned) int` is implementation-defined. – GManNickG Jun 14 '13 at 23:36
  • @BillyONeal: Casting through a union is UB, not IB. Only one member of a union can be active (read from) at a time. – GManNickG Jun 14 '13 at 23:39
  • @GMan: I can't find a reference for that. It may be undefined behavior for some other reason, but 3.10/10 at least seems to say that it isn't UB for aliasing reasons. – Billy ONeal Jun 15 '13 at 00:16
  • @BillyONeal: See http://stackoverflow.com/questions/11373203/accessing-inactive-union-member-undefined. – GManNickG Jun 15 '13 at 00:59
  • @GManNickG: Well, the answer in that question doesn't really make an authoritative statement. It looks like the standard doesn't say one way or the other what happens to me. On one hand, wherever there are ambiguities like this a programmer usually needs to assume any/all interpretations of the standard are possible. On the other hand, in practice I can't think of an implementation that doesn't actually allow type punning through a union like this. – Billy ONeal Jun 15 '13 at 04:27
  • @GManNickG: I guess I'd say it is just like treating `basic_string` as contiguous storage in C++03 to me. Yes, technically the standard didn't require it. But all practical implementations did it. – Billy ONeal Jun 15 '13 at 04:28
  • If we use `reinterpret_cast` will it still be undefined behavior? – David G Jun 18 '13 at 13:36
3

Those do very different things. The first examples interpret the bit-pattern (the "representation") supplied in the integer as a the bit-pattern of a float. The second examples convert the represented number (the "value") of the integer into the float with the closest equivalent value.

http://ideone.com/As73ZB

#include <iostream>

inline float asfloatbits(int x){
    return *(float *) &x;
}

inline float asfloatvalue(int x){
    return (float)x;
}

int main()
{
    int abc = 12341234;
    std::cout << asfloatbits(abc) << std::endl;
    std::cout << asfloatvalue(abc) << std::endl;
}
Billy ONeal
  • 104,103
  • 58
  • 317
  • 552
1

As I understand it, the first one takes a raw integer value and treats it as if it were a float and return it. The second one takes the integer and converts it to the closest equivalent float value and return it.

tangrs
  • 9,709
  • 1
  • 38
  • 53