我需要将一组5U11数字除以6.02
,并且更愿意这样做而不会强制转换为浮动和返回。
5U11表示16位无符号数,11个最低有效位代表小数部分。
我应该如何表示6.02
以及单次计算误差的上限?
答案 0 :(得分:2)
简单缩放100就足够了。
uns16_t x_5U11;
uns32_t acc;
acc = x_5U11;
acc *= 100;
acc += 301; // for round to nearest rather than truncation.
acc /= 602;
错误界限:x_5U11
中的1/2 LSbit。
-
如果速度是最重要的,那么除了执行倍数并按照@alastai的建议划分(通过移动)是要走的路。通过适当的舍入,答案应该在+/- 1 LSBit之内。
如果准确度最重要,则此方法提供+/- 1/2 LSbit(最佳答案)。
[编辑]感谢@Ingo Leonhardt指出我有一个倒置的解决方案。
答案 1 :(得分:2)
这个问题最直接的方法是将6.02的倒数计算为16位数;即计算轮(2 ^ 16 / 6.02)= 0x2a86。请注意,顶部位未设置,因此我们可以选择更高的红利并重新计算以获得更高的准确性;在这种情况下,round(2 ^ 18 / 6.02)= 0xaa1a。
现在,取5U11的数字并进行16x16到32位的加宽乘法,然后向右移(在本例中)为18位,得到你的结果,为5U11值。
例如:
14.3562 * (2^18 / 6.02) = 625148.122 / 2^18 = 2.384
0x72d9 * 0xaa1a = 0x4c4fc40a >> 18 = 0x1313
你确实会以这种方式做一些准确的事情,并且可以稍微改进这种天真的方法(请参阅Henry S. Warren的书 Hacker's Delight ,关于这个主题的更多内容和其他有用的东西)。
显然,如果你的机器能够进行更广泛的乘法运算,你可以将分红的大小增加到2 ^ 18以上,这将提高你的准确度。
如果你想要舍入到最近,你应该添加 d / 2,其中 d 是你的红利(所以在上面的例子中,红利是2 ^ 18,因此舍入值为2 ^ 17或0x20000
。
鉴于域名较小,最简单的方法是进行详尽的搜索以确定最大错误。使用上面的示例并通过添加0x20000
使用舍入到最近,最大错误结果为 x = 0xfa19
:< / p>
0xfa19 * 0xaa1a + 0x20000 = 0xa62e008a >> 18 = 0x298c
实际答案应为
31.2622 / 6.02 = 5.193058
虽然我们的答案是
0x298c * 2^-11 = 5.193359
此实例中的错误为0.000302,或LSB的0.62。
可以选择更具体的舍入常量来最小化误差界限;基本上这可以让我们补偿我们的乘法逆(这里0xaa1a
)不精确的事实。在这个特定的例子中,最佳值似乎在0x1c200
附近,这产生了LSB的0.56的误差范围。