Fly*_*lyq 62 floating-point sqrt type-punning rust
这只是为了满足我自己的好奇心。
是否有此实现:
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;
}
Run Code Online (Sandbox Code Playgroud)
在Rust中?如果存在,则发布代码。
我尝试过但失败了。我不知道如何使用整数格式编码浮点数。这是我的尝试:
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
}
Run Code Online (Sandbox Code Playgroud)
参考:
1 . Quake3快速InvSqrt()的起源-第1页
2. 了解Quake快速逆平方根
3. FAST INVERSE SQUARE ROOT.pdf
4. 源代码:q_math.c#L552-L572
Luk*_*odt 62
我不知道如何使用整数格式编码浮点数。
有一个函数:f32::to_bits返回一个u32。还有另一个方向的函数:f32::from_bits它以u32as作为参数。这些功能优于mem::transmute后者,因为后者unsafe难以使用。
这样,这里是实现InvSqrt:
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)
}
Run Code Online (Sandbox Code Playgroud)
(游乐场)
此函数在x86-64上编译为以下程序集:
.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
Run Code Online (Sandbox Code Playgroud)
我没有找到任何参考程序集(如果有的话,请告诉我!),但是对我来说似乎还不错。我只是不确定为什么将浮点数移入eax以进行移位和整数减法。上交所寄存器可能不支持这些操作吗?
clang 9.0可以-O3将C代码编译为基本相同的程序集。这是一个好兆头。
edw*_*rdw 28
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)
}
}
Run Code Online (Sandbox Code Playgroud)
是否criterion在x86-64 Linux盒子上使用板条箱进行了一些微基准测试。令人惊讶的是,Rust自己sqrt().recip()的速度最快。但是,当然,任何微基准测试结果都应一粒盐。
inv sqrt with transmute time: [1.6605 ns 1.6638 ns 1.6679 ns]
inv sqrt with union time: [1.6543 ns 1.6583 ns 1.6633 ns]
inv sqrt with to and from bits
time: [1.7659 ns 1.7677 ns 1.7697 ns]
inv sqrt with powf time: [7.1037 ns 7.1125 ns 7.1223 ns]
inv sqrt with sqrt then recip
time: [1.5466 ns 1.5488 ns 1.5513 ns]
Run Code Online (Sandbox Code Playgroud)
您可以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
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5420 次 |
| 最近记录: |