This question already has answers here:
John Carmack's Unusual Fast Inverse Square Root (Quake III)
(6 answers)
Closed 5 years ago.
I saw following code here.
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the heck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
I don't understand following line.
i = * ( long * ) &y;
Generally, we use * and & with pointer, but here both used with variable. So, what does it do here?
The line is taking a float, looking at the memory holding that float, reinterpreting that memory as memory holding a long, and getting that long. Basically, it's reinterpreting the bit-pattern of a floating point number as that of an integer, in order to mess around with its bits.
Unfortunately, that code is also wrong. You are not allowed to dereference that casted pointer, for reasons described here. In C, the one-and-only way of reinterpreting a bit pattern is through memcpy. (Depending on C variant and implementation, going through a union may be acceptable as well.)
First, a disclaimer: This is technically undefined behavior because it violates the strict aliasing rule, but most compilers will do the below, and I don’t know what the standards situation was when this was first written.
When you look at the expression, there are four main parts:(
y is the float variable we want to convert. Simple enough.
& is the usual address-of operator, so &y is a pointer to y.
(long *) is a cast to a pointer to long, so (long *) &y is a pointer-to-long pointing to the same location in memory as y is at. There is no real long there, just a float, but if both float and long are 32 bits (like the code assumes), this will give you a pointer to a long with the same bit pattern as a float.
Finally, * dereferences the pointer. Thus, the full expression, * ( long * ) &y;, gives you a long with same bit pattern as y.
Usually, a long with the same bit pattern as a float would be useless, because they store numbers in completely different ways. However, it’s easier to do bit manipulation to a long, and the program later converts it back to a `float.
It means the address of y (making it a pointer) cast to a long pointer, dereferenced and assigned to i.
Related
I was "playing" with the fast inverse sqrt function by trying to optimize certain things (fewer variables etc). Here is the final code:
float Q_rsqrt(float x) {
const float x2 = x * 0.5F;
uint_fast32_t i;
memcpy(&i, &x, sizeof(float));
i = 0x5f3759df - ( i >> 1 );
memcpy(&x, &i, sizeof(float));
return x * ( 1.5F - ( x2 * x * x ) );
}
First of all, it is useful to know that on my architecture uint_fast32_t is represented on 64 bits and float on 32 bits. It can therefore be surprising to make memcpy() on variable types of different sizes.
The problem I have once the code is compiled, a call to this function with the same argument each time gives the same return value : sometimes this one is negative, other times positive (but always of the same absolute value).
The usefulness of memcpy() is to bypass the warning (dereferencing type-punned pointer will break strict-aliasing rules) of the following code (which works exactly as desired) :
float Q_rsqrt_test(float x) {
const float xHalf = x * 0.5F;
const uint_fast32_t i = 0x5f3759df - ( (* ( uint_fast32_t * ) &x) >> 1 );
x = * ( float * ) &i;
return x * ( 1.5F - ( xHalf * x * x ) );
}
For this code, I want to say that there is no problem of type size because already the source (visible on the link just above) uses a double type, which is represented on 64 bits on my architecture (instead of float on 32 bits).
I really can't understand why I can get negative answers with the first function...
Thanks for the help.
As Barmar said, initializing i to 0 works. I also think it's better to avoid dereferecing to a type with another size (for example *(uint64_t *) &x where x is a float). Probably better using dereferecing with uint32_t and then properly cast the result to a 64 bits variable.
If I have a variable as float var1 = 157.1; and want to transform it in int I would do int var2 = (int)var1;
I want to know about the other types of data, such as long int, short int, unsigned short int, long double and so on.
I tried long int var2 = (long int)var1; and it seemed to work, but I'm not sure if it is syntactically correct. If it is, I assume it'd be the same for all the other types, i.e., just the data type and its attributes separated by a space. If it isn't I'd like to know if there's a list of them of some sort.
This is the C cast operator, but the operation is more generally "type casting", "casting", or "recasting". This is a directive to the compiler to request a specific conversion.
When casting any valid C type can be specified, so:
int x = 10;
unsigned long long y = (unsigned long long) x;
In many cases this conversion can be done implicitly, automatically, so it's not always necessary but in others you must force it. For example:
int x = 10;
float y = x; // Valid, int -> float happens automatically.
You can get caught by surprise though:
int x = 10;
float y = x / 3; // y = 3.0, not 3.333, since it does integer division before casting
Where you need to cast to get the right result:
int x = 10;
float y = (float) x / 3; // 3.33333...
Note that when using pointers this is a whole different game:
int x = 10;
int* px = &x;
float* y = (float*) px; // Invalid conversion, treats int as a float
Generally C trusts you to know what you're doing, so you can easily shoot yourself in the foot. What "compiles" is syntactically valid by definition, but executing properly without crashing is a whole other concern. Anything not specified by the C "rule book" (C standard) is termed undefined behaviour, so you'll need to be aware of when you're breaking the rules, like in that last example.
Sometimes breaking the rules is necessary, like the Fast Inverse Square Root which relies on the ability of C to arbitrarily recast values.
I see some code in c like this
int main()
{
int x = 4, y = 6;
long z = (long) x + y;
}
what is the benefit of casting even though in this case it implicit? Which operation comes first x + y or casting x first?
In this case the cast can serve a purpose. If you wrote
int x = 4, y = 6;
long z = x + y;
the addition would be performed on int values, and then, afterwards, the sum would be converted to long. So the addition might overflow. In this case, casting one operand causes the addition to be performed using long values, lessening the chance of overflow.
(Obviously in the case of 4 and 6 it's not going to overflow anyway.)
In answer to your second question, when you write
long z = (long)x + y;
the cast is definitely applied first, and that's important. If, on the other hand, you wrote
long z = (long)(x + y);
the cast would be applied after the addition (and it would be too late, because the addition would have already been performed on ints).
Similarly, if you write
float f = x / y;
or even
float f = (float)(x / y);
the division will be performed on int values, and will discard the remainder, and f will end up containing 0. But if you write
float f = (float)x / y;
the division will be performed using floating-point, and f will receive 0.666666.
This question already has answers here:
John Carmack's Unusual Fast Inverse Square Root (Quake III)
(6 answers)
Closed 8 years ago.
I found a very complex function this is an implementation of Fast inverse square root. I honestly do not understand how this function works but the following conversion between a long and a float has caught my eye:
i = *(long *) &y;
And I leave the full code
inline float Q_rsqrt(float number)
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = *(long *) &y;
i = 0x5f3759df - (i >> 1);
y = * (float *) &i;
y = y * (threehalfs - (x2 * y * y));
return y;
}
The cast simply reinterprets the bits of y as a long so that it can perform integer arithmetic on them.
See Wikipedia for an explanation of the algorithm: Fast inverse square root.
The code makes use of the knowledge that, on the target platform, sizeof(long) == sizeof(float).
#R.. also helpfully adds the following in a comment:
It's also invalid C -- it's an aliasing violation. A correct version of this program needs use either memcpy or possibly (this is less clear that it's correct, but real compilers document support for it) union-based type punning. The version in OP's code will definitely be "miscompiled" (i.e. in a way different than the author's intent) by real compilers though.
This means that the code is not only architecture-specific, it is also compiler-specific.
float FastInvSqrt(float x) {
float xhalf = 0.5f * x;
int i = *(int*)&x; // evil bit-level floating-point hacking
i = 0x5f3759df - (i >> 1); // what the...?
x = *(float*)&i;
x = x*(1.5f-(xhalf*x*x));
return x;
}
There are many places to read about this on the internet, but they all skip over the line:
int i = *(int*)&x;
Could somebody please explain it to me?
It means: take the address of the variable x (whatever type it may be), cast that address to an int pointer then dereference that to get an int from that address.
Technically, I think it's undefined behaviour but it works fine in many implementations. I suspect the author didn't really care that much, based on the non-readability of the code :-) They could at least have documented the method, even if with only a URL.
The cast after the integer manipulation line (the one you've so elegantly described with WTF) is similar as it goes from int back to float.