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.
Related
I'm using avr-gcc where sizeof(double) and sizeof(float) are both 4 and I'm having an issue with double arithmetic to get the correct integer result:
// x is some value between 8.0 and 9.6103
double x = 9.6103;
uint32_t r = pow(x,2) * 8813377.768984962;
The correct value of r should be 813984763 rounded down but the actual result is 813984768.
How can I get the correct integer result?
I've tried to split the calculation like this:
uint32_t r1 = pow(x,2) * 8813;
double d1 = pow(x,2) * .77768984962;
uint32_t r = r1 + d1;
But this still suffers from precision issues i.e I can't seem to get 813984763 exactly and I'm only interested in that the integer part of the result is correct. Any ideas?
A float cannot represent the precision you need for this value (813984763), much less for the calculation, and as you've noted avr-gcc has wrongly redefined double to be the same as float.
The closest representable values in float are:
Below: 813984704
Above: 813984768 (closer)
You could scale it up and use 128-bit integers to do the arithmetic. 128-bit is soooo much you could just multiply it all to integers.
double x = 9.6103;
uint128_t y = x * 10000; // = 96103 / 10000
uint128_t c = 8813377768984962; // = 8813377.768984962 * 1000000000
uint32_t r = y * y * c / 10000/10000 /1000000000;
// max y * y * c = 96103 * 96103 * 8813377768984962 =
// = 81398476378849607561973858
// UINT128_MAX = 340282366920938463463374607431768211456
// ^^ is way more, so it will not overflow.
Your platform most probably does not support __uint128_t GCC extension, so you could write your own library for that. There are endless 128-bit libraries in C++ on github - port one to C (or find one in C) and use it.
Well, I got some free time and I always wanted to have a C uint128 library, so I took this library https://github.com/calccrypto/uint128_t and ported it to C and wrote an executable that does the same computations as presented above and compiled it for atmega128 with avr-gcc -Os and run avr-nm -td --sort-size over the result. These are the biggest 5 symbols in the result and the whole program has ~12KB of .text. So, a bit of space is needed for this solution to work.
00000642 T how_to_split_double_multiplication
00000706 T kuint128_rshift
00000762 T kuint128_lshift
00003104 T kuint128_mul
00004594 T kuint128_divmod
This question already has answers here:
C program division error?
(7 answers)
Closed 3 years ago.
I am working with STM32 and GCC Compiler and I make the following division:
uint8_t w, h;
w=2;
h=5;
float test = (w * h) / 8;
and the result is test=1
Why am I wrong? I cannot understand this behavior.
Could someone explain me the reason?
(w * h) / 8 is an integer expression with an integer result.
The assignment to float is an implicit cast, but will not recover the lost fractional part.
You can make the division a floating point expression by ensuring that at least one of the operands is floating point:
float test = (w * h) / 8.0f ;
You need to do the following modification if you want to get the correct result:
uint8_t w, h;
w = 2;
h = 5;
float test = (float)(w * h) / 8;
If you divide integers, you get an integer as a result.
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.
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.
This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
John Carmack’s Unusual Fast Inverse Square Root (Quake III)
I came across this piece of code a blog recently - it is from the Quake3 Engine. It is meant to calculate the inverse square root fast using the Newton-Rhapson method.
float InvSqrt (float x){
float xhalf = 0.5f*x;
int i = *(int*)&x;
i = 0x5f3759df - (i>>1);
x = *(float*)&i;
x = x*(1.5f - xhalf*x*x);
return x;
}
What is the reason for doing int i = *(int*)&x;? Doing int i = (int) x; instead gives a completely different result.
int i = *(int*)&x; doesn't convert x to an int -- what it does is get the actual bits of the float x, which is usually represented as a whole other 4-byte value than you'd expect.
For reference, doing this is a really bad idea unless you know exactly how float values are represented in memory.
int i = *(int*)&x; says "take the four bytes which make up the float value x, and treat them as if they were an int." float values and int value are stored using completely different methods (e.g. int 4 and float 4.0 have completely different bit patterns)
The number that ends up in i is the binary value of the IEEE floating point representation of the number in x. The link explains what that looks like. This is not a common C idiom, it's a clever trick from before the SSE instructions got added to commercially available x86 processors.