是否可以在Rust中编写Quake的快速InvSqrt()函数?

问题描述 投票:3回答:3

这只是为了满足我的好奇心。

是否有此实现:

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;
}

在Rust中?如果存在,则发布代码。

我尝试过,但失败了。我不知道如何使用整数格式对浮点数进行编码。

参考:1.Origin of Quake3's Fast InvSqrt() - Page 12.Understanding Quake’s Fast Inverse Square Root3. FAST INVERSE SQUARE ROOT.pdf

rust sqrt
3个回答
2
投票

您可以使用std::mem::transmute进行所需的转换:

fn inv_sqrt(x: f32) -> f32 {
    let xhalf = 0.5f32 * x;
    let mut i: i32 = unsafe { std::mem::transmute(x) };
    i = 0x5f3759df - (i >> 1);
    let mut res: f32 = unsafe { std::mem::transmute(i) };
    res = res * (1.5f32 - xhalf * res * res);
    res
}

您可以在此处找到一个实时示例:here


2
投票

我不知道如何使用整数格式对浮点数进行编码。

具有以下功能:f32::to_bits返回f32::to_bits。另一个方向也有该功能:u32,以f32::from_bits作为参数。这些功能优于f32::from_bits,因为后者是u32,使用起来比较棘手。

因此,这是mem::transmute的实现:

unsafe

InvSqrt


此函数在x86-64上编译为以下程序集:

fn inv_sqrt(x: f32) -> f32 {
    let i = x.to_bits();
    let i = 0x5f3759df - (i >> 1);
    let y = f32::from_bits(i);

    y * (1.5 - 0.5 * x * y * y)
}

我没有找到任何参考程序集(如果有的话,请告诉我!),但是对我来说似乎还不错。我只是不确定为什么将浮点数移到Playground只是为了进行移位和整数减法。也许SSE寄存器不支持这些操作?

带有.LCPI0_0: .long 3204448256 ; f32 -0.5 .LCPI0_1: .long 1069547520 ; f32 1.5 example::inv_sqrt: movd eax, xmm0 shr eax ; i << 1 mov ecx, 1597463007 ; 0x5f3759df sub ecx, eax ; 0x5f3759df - ... movd xmm1, ecx mulss xmm0, dword ptr [rip + .LCPI0_0] ; x *= 0.5 mulss xmm0, xmm1 ; x *= y mulss xmm0, xmm1 ; x *= y addss xmm0, dword ptr [rip + .LCPI0_1] ; x += 1.5 mulss xmm0, xmm1 ; x *= y ret 的clang 9.0,将C代码编译为eax。因此,这是一个好兆头。


1
投票

@ RealFresh答案的补充,其中用Rust中鲜为人知的-O3实现它:

basically the same assembly

[使用union设置了一些微基准测试。令人惊讶的是,Rust自己的union FI { f: f32, i: i32, } fn inv_sqrt(x: f32) -> f32 { let mut u = FI { f: x }; unsafe { u.i = 0x5f3759df - (u.i >> 1); u.f * (1.5 - 0.5 * x * u.f * u.f) } } 是最快的。但是,当然,任何微基准测试结果都应一粒盐。

criterion
© www.soinside.com 2019 - 2024. All rights reserved.