This is just to satisfy my own curiosity.
Is there an implementation of this:
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;
}
in Rust? If it exists, post the code.
I tried it and failed. I don't know how to encode the float number using integer format. Here is my attempt:
fn main() {
println!("Hello, world!");
println!("sqrt1: {}, ",sqrt2(100f64));
}
fn sqrt1(x: f64) -> f64 {
x.sqrt()
}
fn sqrt2(x: f64) -> f64 {
let mut x = x;
let xhalf = 0.5*x;
let mut i = x as i64;
println!("sqrt1: {}, ", i);
i = 0x5f375a86 as i64 - (i>>1);
x = i as f64;
x = x*(1.5f64 - xhalf*x*x);
1.0/x
}
Reference:
1. Origin of Quake3's Fast InvSqrt() - Page 1
2. Understanding Quake’s Fast Inverse Square Root
3. FAST INVERSE SQUARE ROOT.pdf
4. source code: q_math.c#L552-L572
There is a function for that:
f32::to_bits
which returns anu32
. There is also the function for the other direction:f32::from_bits
which takes anu32
as argument. These functions are preferred overmem::transmute
as the latter isunsafe
and tricky to use.With that, here is the implementation of
InvSqrt
:(Playground)
This function compiles to the following assembly on x86-64:
I have not found any reference assembly (if you have, please tell me!), but it seems fairly good to me. I am just not sure why the float was moved into
eax
just to do the shift and integer subtraction. Maybe SSE registers do not support those operations?clang 9.0 with
-O3
compiles the C code to basically the same assembly. So that's a good sign.It is worth pointing out that if you actually want to use this in practice: please don't. As benrg pointed out in the comments, modern x86 CPUs have a specialized instruction for this function which is faster and more accurate than this hack. Unfortunately,
1.0 / x.sqrt()
does not seem to optimize to that instruction. So if you really need the speed, using the_mm_rsqrt_ps
intrinsics is probably the way to go. This, however, does again requireunsafe
code. I won't go into much detail in this answer, as a minority of programmers will actually need it.This one is implemented with less known
union
in Rust:Did some micro benchmarks using
criterion
crate on a x86-64 Linux box. Surprisingly Rust's ownsqrt().recip()
is the fastest. But of course, any micro benchmark result should be taken with a grain of salt.You may use
std::mem::transmute
to make needed conversion:You can look for a live example here: here