【问题标题】:Efficiently dividing a double by a power of 2有效地将双精度数除以 2 的幂
【发布时间】:2012-01-19 13:21:12
【问题描述】:

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

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 汇编解决方案就可以了。

【问题讨论】:

  • 你确定它有那么重要吗?使用机器特定的位操作是不可移植的,甚至可能因为缓存或调度问题而影响性能......
  • @BasileStarynkevitch 这就是我最后一段的意思。假设 IEEE 754,我不想进行位操作,但使用特定于 x86 的汇编解决方案就可以了。
  • 我不会为此烦恼。你不会赢得太多,而且你可能会失去一些表现。
  • 如果你开启了优化,编译器应该已经用乘以倒数代替了除法。
  • 请发布替代功能。

标签: c++ c x86


【解决方案1】:

用倒数声明一个变量(或常数)并乘以它,有效地将除法更改为乘法:

static const double div_2_pow_30 = 1.0 / 1073741824.0;

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

【讨论】:

  • 我想说编译器可以解决这个问题,但实际上没有,it could lead to different numerical results(除非你使用例如 -ffast-math 来表示 gcc)。
  • @spraff:如果除数是 2 的精确幂,并且其倒数不会上溢或下溢,则结果始终相同。大多数编译器都意识到了这一点,并进行了优化。
  • 你的意思是const double f = 1.0 / 1073741824.0;,当然。就像,如果它是一个常数,让它const!
  • 我现在因为不尝试这个而感到愚蠢,但是是的,就是这样。带有 -O2 的 GCC 4.4.3 显然不会自动对此进行优化。
  • 当你说“用位操作修改尾数”时,你的意思是指数,对吧?
【解决方案2】:

我不确定您是否可以信任此处的分析。对于更小、更快的函数,分析代码本身的效果开始扭曲结果。

在循环中运行noiseValueDouble 和相应的替代方案以获得更好的数字。

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

如果你真的想使用特殊的指令,MMX 之类的。

【讨论】:

  • 是的,我也这样做了。效果虽然不那么明显,但仍然存在。对于梯度噪声,调用是grad(noiseValueInt(seed, x0, y0), x, y),对于值噪声,调用是noiseValueDouble(seed, x0, y0)。除此之外,这两种算法是相同的。
【解决方案3】:

您可以直接使用frexp和ldexp函数修改指数。我不确定这是否会更快。

【讨论】:

    【解决方案4】:

    我尝试用 gcc 编译它:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-03
      • 1970-01-01
      • 1970-01-01
      • 2014-07-25
      • 1970-01-01
      相关资源
      最近更新 更多