我正在尝试将时间戳(仅秒的小数部分)从纳秒(10 ^ -9秒的单位)重新缩放到NTP时间戳的下半部分(单位为2 ^ -32秒)。实际上这意味着乘以4.2949673。但是我需要在没有浮点数学的情况下完成它,并且不使用大于32位的整数(事实上,我实际上是为8位微控制器编写这个,所以即使32位数学也很昂贵,特别是划分)。
我提出了一些运行良好的算法,但我没有任何数学方法的真正基础,所以我很欣赏任何有关如何改进它们的建议,或任何其他算法更准确和/或更快。
uint32_t intts = (ns >> 16) * 281474 + (ns << 16) / 15259 + ns / 67078;
选择前两个常数略微下冲,而不是超过正确的数字,并且根据经验确定最终因子67078以纠正此问题。产生+/- 4 NTP单位的正确值,结果为+/- 1 ns - 可接受,但残差随ns
变化。我想我可以添加另一个术语。
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是根据经验确定的,以消除截断所有低阶位的下冲,但它没有做到最好的工作。
答案 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
在班次中处理。编译器通常可以为这类事物生成相当不错的代码,但如果需要,通常可以通过手写汇编来简化。
如果你不需要那么多的准确性,你可以摆脱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;
}
答案 1 :(得分:1)
我可能会说明显而易见的......但是你有用Google搜索定点数学库的interwebz吗?有很多。在Flipcode的档案中,这是一个很好的C ++和x86实现: