1

I saw this cast:

const std::uint32_t tmp = (some number);
float ToFloat = *(float*)(&tmp);

But when casting, the wrong number gets into the float. Moreover, if I use a static cast, then it is cast correctly.

  1. Why is this happening?
  2. How exactly does static_cast work?
  3. *(float*)(&Tmp) means that we are trying to dereference the pointer to the float, which is located at the address &tmp. Is it right?
user3840170
  • 26,597
  • 4
  • 30
  • 62
RookieCPP
  • 83
  • 7
  • What is a "C" style cast doing in a C++ program anyway? Whatever you've seen is not recommended just use : `auto value = static_cast(tmp);` So I am curious where have you seen it? Since it doesn't seem to be a good reference to learn C++ from – Pepijn Kramer Dec 18 '22 at 14:32
  • The second statement actually does `float ToFloat = *reinterpret_cast(const_cast(&Tmp));`, i.e. you tell the compiler to ignore anything it knows about the actual type of the expression `&Tmp` (`uint32 const*`) and simply turn the pointer into a pointer to `float`. The data the pointer refers to of course is not actually a float and the result is undefined behaviour. Simply `static_cast` ing from `uint32` to `float` allows the compiler to do the proper conversion of the value though. – fabian Dec 18 '22 at 14:33
  • 1
    This looks like it was copied straight from the [fast inverse square root](https://en.wikipedia.org/wiki/Fast_inverse_square_root) - where it was used for a very specific purpose (and has been outdated for over 20 years) – UnholySheep Dec 18 '22 at 14:35
  • 2
    The intent is not to get the right value in to the float (a simple cast does that as you say), instead the intent is to assign the bit pattern directly from `Tmp` to `ToFloat`. Presumably `Tmp` has bits that correspond to a desired float. Therefore to understand the code you would need to understand how floating point values are stored as bit patterns. – john Dec 18 '22 at 14:41

3 Answers3

2

Why is this happening?

The program has undefined behavior because you read from an int through the eyes of a float. This is not allowed in C++.

How exactly does static_cast work?

In this case, it does the same conversion that float ToFloat = Tmp; would do. That is, it converts the int to float and assigns it to ToFloat.

*(float*)(&Tmp) means that we are trying to dereference the pointer to the float, which is located at the address &Tmp. Is it right?

There is no float at &Tmp, only an int. You however tell the compiler that the pointer is pointing at a float and dereference that pointer. The bit patterns of ints and floats are very different so not only does it have undefined behavior, you will very unlikely get the correct result.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • "_you will very unlikely get the correct result_": Wherever OP got the code from probably intents exactly that reinterpreting behavior. But it is either code that is assumed to be compiled in a specific manner (e.g. with the `-fno-strict-aliasing` flag), specific for some compiler that allows the aliasing (I think MSVC does) or very outdated relying on the compiler simply behaving as intended regardless of the UB. – user17732522 Dec 18 '22 at 14:53
  • Correct or not it's likely that the intent of the code was to assign a floating point bit pattern composed in `Tmp` to an actual floating point variable. – john Dec 18 '22 at 14:55
  • Could be. OP says that a `static_cast` does the correct thing though and in that case `*(float*)(&Tmp)` will not. – Ted Lyngmo Dec 18 '22 at 14:55
0

Storing Integers

Integers are stored in their binary (base 2) representation. int32_t occupies 32 bits.

Storing Floats

In most of the C++ compilers use a standard called IEEE 754, to store floats and doubles. Which differs a lot from how integers are stored.

You can check how numbers are stored yourself using some sort of a IEEE-754 Floating Point Converter.

Example

Let us consider a simple example of number 5.

const uint Tmp = 5;

Tmp would be stored as 0x00000005 in memory, therefore &Tmp would point to the same.

float ToFloat = *(float*)(&Tmp);

Casting it into a float pointer would treat 0x00000005 to be in IEEE 754 format, having value 7.00649232162e-45.

static_cast

The static cast performs conversions between compatible types using safe implicit conversions.

Note that you are converting from int* to float* in your question which is not a safe conversion. You can convert from int to float safely.

const uint32_t Tmp = 5;
float ToFloat = (float)Tmp; // 5

More about static_cast conversion.

Aryan
  • 24
  • 1
  • 4
0

What this code attempts to do is to convert a 32-bit representation of a floating-point number into an actual floating-point value. An IEEE 754 binary floating-point value is (most of the time) a representation of a number of the form (−1)s × m × 2e. For a given floating-point format, a certain number of bits within a machine word is allocated for each of s (the sign bit), m (the mantissa or significand bits) and e (the exponent bits). Those bits can be manipulated directly within the bit representation in order to construct any possible floating-point value, bypassing the usual abstractions of ordinary arithmetic operations provided by the language. The converter mentioned in Aryan’s answer is a pretty good way to explore how this works.

Unfortunately, this code is not standard-conforming C++ (or, in fact, C), and that’s thanks to a little something called strict aliasing. In short: if a variable has been declared as being of integer type, it can only be accessed via pointers to that same integer type. Accessing it via a pointer to a different type is not allowed.

A more-or-less officially-sanctioned version would be:

inline static float float_from_bits(std::uint32_t x) {
    float result;
    static_assert(sizeof(result) == sizeof(x),
        "float must be 32 bits wide");
    std::memcpy(&result, &x, sizeof(result));
    return result;
}

Or, in C++20, simply std::bit_cast<float>(x). This is still not quite guaranteed to return expected results by the standard, but at least it doesn’t violate that particular rule.

A direct static_cast would instead convert the integer into a floating-point value denoting (approximately) the same quantity. This is not a direct bit conversion: performing it entails computing the appropriate values of s, m and e and putting them in the appropriate place according to the number format.

user3840170
  • 26,597
  • 4
  • 30
  • 62