定点乘法算法

我正在尝试将时间戳(仅秒的小数部分)从纳秒(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是凭经验确定的,以消除截断所有低阶位的下冲,但它没有做到最好的工作。     
已邀请:
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;
}
基本定点乘法,使用四个16x16 - > 32个产品模拟32x32 - > 64产品。选择常数
round
以使用简单的二进制搜索来最小化误差。在整个有效范围内,该表达式对于+/- 0.6 NTP是好的。 比例因子中的前导
4
在班次中处理。编译器通常可以为这类事物生成相当不错的代码,但如果需要,通常可以使用手写汇编来简化。 如果你不需要那么多精确度,你可以摆脱
lowTerm
round
并得到一个好的+/- 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;
}
    
我可能会说明显而已......但你有没有用Google搜索固定点数学库的interwebz?有很多。在Flipcode的档案中,这是一个很好的C ++和x86实现: http://www.flipcode.com/archives/Fixed_Point_Routines.shtml     

要回复问题请先登录注册