有效地将双精度数除以 2 的幂

问题描述 投票:0回答:4

我正在实现相干噪声函数,并惊讶地发现使用梯度噪声(即 Perlin 噪声)实际上比值噪声稍快。分析显示,其原因是将随机 int 值转换为 -1.0 到 1.0 范围内的双精度值所需的除法:

static double noiseValueDouble(int seed, int x, int y, int z) {
    return 1.0 - ((double)noiseValueInt(seed, x, y, z) / 1073741824.0);
}

梯度噪声需要更多的乘法,但由于预先计算的梯度表使用

noiseValueInt
直接计算表中的索引,并且不需要任何除法。所以我的问题是,考虑到除法是按 2 的幂 (2^30) 进行除法,如何才能使上述除法更有效。

理论上,需要做的就是从双精度数的指数中减去 30,但是通过强力(即位操作)来做到这一点会导致各种极端情况(INF、NAN、指数溢出等)。 x86 汇编解决方案就可以了。

c++ c x86
4个回答
6
投票
声明一个具有倒数的变量(或常量)并乘以它,有效地将除法更改为乘法:

static const double div_2_pow_30 = 1.0 / 1073741824.0;

另一种方法(利用数字是2的幂的性质)是通过位运算修改指数。这样做将使代码依赖于使用 IEEE 标准存储的双精度数,这可能不太可移植。


4
投票
您可以直接使用函数修改指数:

我不确定这是否会更快。


2
投票
我不确定您是否可以相信这里的分析。对于更小、更快的函数,分析代码本身的影响开始扭曲结果。

循环运行

noiseValueDouble

 和相应的替代方案以获得更好的数字。

一个 x86 汇编器解决方案

一个位摆弄解决方案,您也可以在 C 中进行位摆弄。快速二的幂除法指令(位移位)仅适用于整数。

如果你真的想使用特殊指令,MMX 或其他什么。


0
投票
我尝试用 gcc 编译它:

double divide(int i) { return 1.0 - (double)i / 1073741824.0; }

使用

-O3

,它被编码为 
FMULS
 指令,使用 
-O3 -mfpmath=sse -march=core2
,它使用 SSE 指令集并将其编码为 
MULSD
。我不知道什么是最快的,但函数调用本身可能比实际除法慢几个数量级。

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