定点乘法算法

时间:2011-03-03 23:50:30

标签: c numerical-methods fixed-point

我正在尝试将时间戳(仅秒的小数部分)从纳秒(10 ^ -9秒的单位)重新缩放到NTP时间戳的下半部分(单位为2 ^ -32秒)。实际上这意味着乘以4.2949673。但是我需要在没有浮点数学的情况下完成它,并且不使用大于32位的整数(事实上,我实际上是为8位微控制器编写这个,所以即使32位数学也很昂贵,特别是划分)。

我提出了一些运行良好的算法,但我没有任何数学方法的真正基础,所以我很欣赏任何有关如何改进它们的建议,或任何其他算法更准确和/或更快。

算法1

uint32_t intts = (ns >> 16) * 281474 + (ns << 16) / 15259 + ns / 67078;

选择前两个常数略微下冲,而不是超过正确的数字,并且根据经验确定最终因子67078以纠正此问题。产生+/- 4 NTP单位的正确值,结果为+/- 1 ns - 可接受,但残差随ns变化。我想我可以添加另一个术语。

算法2

uint32_t ns2 = (2 * ns) + 1;
uint32_t intts = (ns2 << 1)
  + (ns2 >> 3) + (ns2 >> 6) + (ns2 >> 8) + (ns2 >> 9) + (ns2 >> 10)
  + (ns2 >> 16) + (ns2 >> 18) + (ns2 >> 19) + (ns2 >> 20) + (ns2 >> 21)
  + (ns2 >> 22) + (ns2 >> 24) + (ns2 >> 30) + 3;

基于4.2949673的二进制扩展(实际上基于2.14748365的二进制扩展,因为我开始加倍并添加一个来完成舍入)。可能比算法1快(我还没有达到基准测试)。 +3是根据经验确定的,以消除截断所有低阶位的下冲,但它没有做到最好的工作。

2 个答案:

答案 0 :(得分:8)

uint32_t convert(uint32_t x) {
    const uint32_t chi = 0x4b82;
    const uint32_t clo = 0xfa09;
    const uint32_t round = 0x9525;
    const uint32_t xhi = x >> 16;
    const uint32_t xlo = x & 0xffff;
    uint32_t lowTerm = xlo*clo;
    uint32_t crossTerms = xhi*clo + xlo*chi;
    uint32_t rounded = crossTerms + (lowTerm >> 16) + round >> 16;
    uint32_t highTerm = xhi*chi;
    return (x << 2) + highTerm + rounded;
}

基本定点乘法,模拟32x32 - &gt; 64个产品使用四个16x16 - &gt; 32个产品。选择常量round以使用简单的二进制搜索来最小化错误。在整个有效范围内,该表达式对于+/- 0.6 NTP是好的。

比例因子中的前导4在班次中处理。编译器通常可以为这类事物生成相当不错的代码,但如果需要,通常可以通过手写汇编来简化。

如果你不需要那么多的准确性,你可以摆脱lowTermround并得到一个好于+/- 1.15 NTP的答案:

uint32_t convert(uint32_t x) {
    const uint32_t chi = 0x4b82;
    const uint32_t clo = 0xfa09;
    const uint32_t xhi = x >> 16;
    const uint32_t xlo = x & 0xffff;
    uint32_t crossTerms = xhi*clo + xlo*chi;
    uint32_t highTerm = xhi*chi;
    return (x << 2) + highTerm + (crossTerms >> 16) + 1;
}

答案 1 :(得分:1)

我可能会说明显而易见的......但是你有用Google搜索定点数学库的interwebz吗?有很多。在Flipcode的档案中,这是一个很好的C ++和x86实现:

http://www.flipcode.com/archives/Fixed_Point_Routines.shtml