更快找到倍数的方法

时间:2010-06-21 14:08:37

标签: c optimization floating-point

如果具有以下C函数,则用于确定一个数字是否是另一个数字与arbirary容差的倍数

#include <math.h>

#define TOLERANCE 0.0001

int IsMultipleOf(double x,double mod)
{
    return(fabs(fmod(x, mod)) < TOLERANCE);
}

它工作正常,但分析显示它非常慢,只要它已成为优化的候选者。约有75%的时间用于modulo,其余时间用于fabs。我正在试图找到一种加快速度的方法,使用像查找表这样的东西。参数x会定期更改,而mod不经常更改。 x的可能值的数量足够小,使得查找的空间不会成为问题,通常它将是几百个可能值中的一个。我可以很容易地摆脱fabs,但无法找出模数的合理替代方案。关于如何优化上述的任何想法?

编辑该代码将在各种Windows桌面和移动设备上运行,因此处理器可能包括英特尔,AMD在桌面上,以及ARM或SH4在移动设备上。 VisualStudio 2008是编译器。

8 个答案:

答案 0 :(得分:3)

你真的必须使用modulo吗?

是不是可以只result = x / mod,然后检查result的小数部分是否接近0.例如:

11 / 5.4999 = 2.000003  ==> 0.000003 < TOLERANCE 

或类似的东西。

答案 1 :(得分:1)

除法(在你的情况下是浮点,fmod)通常是一个操作,执行时间根据cpu和编译器的不同而变化很多:

  • gcc有一个内置的替代品 如果你给它正确的编译 标记或使用__builtin_fmod 明确。然后这可能会映射 操作上少数 汇编指令。
  • 可能会有像SSE这样的特殊单位 在intel处理器上这个 操作更多 有效地

通过这些技巧,取决于你的环境(你没有告诉哪个),时间可能会从一些时钟周期变化到几百个。我认为最好是查看编译器和cpu的文档以了解该特定操作。

答案 2 :(得分:1)

以下可能是矫枉过正,并且次优。但是,这里有什么值得一提的方法。

我们知道双重格式...

  • 1位用于标志
  • 偏置指数的11位
  • 52分数位

让...

  • value = x / mod;
  • exp =指数位值 - BIAS;
  • lsb =值的分数位的最小sig位;

一旦你拥有了......

/*
 * If applying the exponent would eliminate the fraction bits
 * then for double precision resolution it is a multiple.
 * Note: lsb may require some massaging.
 */
if (exp > lsb)
    return (true);

if (exp < 0)
    return (false);

剩下的唯一案例是容忍案例。建立你的双倍,以便你摆脱小数点左边的所有数字。

  • 符号位为零(正)
  • 指数是BIAS(1023我认为......看起来可以肯定)
  • 适当地移动分数位

现在将它与您的容忍度进行比较。

答案 3 :(得分:1)

我认为您需要检查C RTL fmod()函数的大小:X86 FPU有'FPREM/FPREM1'指令,通过重复减法计算余数。

虽然浮点除法是单指令,但似乎您可能需要重复调​​用FPREM以获得模数的正确答案,因此您的RTL可能不会使用它。

答案 4 :(得分:1)

我根本没有测试过这个,但从我理解fmod的方式来看,这应该是等效内联的,这可能让编译器更好地优化它,尽管我会认为编译器的数学库(或内置函数)只能工作同样。 (另外,我甚至不确定这是否正确)。

#include <math.h>

int IsMultipleOf(double x, double mod) {
    long n = x / mod;  // You should probably test for /0 or NAN result here
    double new_x = mod * n;
    double delta = x - new_x;
    return fabs(delta) < TOLERANCE;  // and for NAN result from fabs
}

答案 5 :(得分:1)

如果你有相当规模的数据,也许你可以用很长的时间而不是双倍。例如long long就足以超过60 astronomical units in micrometer resolution

答案 6 :(得分:0)

需要双精度吗?根据你的数学库有多好,这应该更快:

#include <math.h>

#define TOLERANCE 0.0001f

bool IsMultipleOf(float x, float mod)
{
    return(fabsf(fmodf(x, mod)) < TOLERANCE);
}

答案 7 :(得分:0)

我认为modulo在内部看起来有点像这样:

mod(x,m) {
  while (x > m) {
    x = x - m
  }
  return x
}

我认为通过某种搜索我可以进行优化:例如:

fastmod(x,m) {
  q = 1

  while (m * q < x) {
    q = q * 2
  }

  return mod((x - (q / 2) * m), m)
}

您甚至可以选择将finall调用替换为mod,并调用fastmod,添加条件:如果x&lt;然后返回x。