作为一个学习项目,我使用c ++在软件中实现浮点运算(add,sub,mul,div)。目标是更加熟悉浮点行为的底层细节。
我正在尝试将我的处理器操作与精确位匹配,这意味着IEEE 754标准。到目前为止,它一直运行良好,添加,子和多种行为完美,我在大约1.1亿个随机操作上测试它,并获得与处理器硬件相同的结果。 (虽然没有考虑边缘情况,溢出等)。
之后,我开始转到最后一次操作,师。它工作正常并达到想要的结果,但有时,我得到最后一个尾数位错误,而不是四舍五入。我有点难以理解为什么。 我一直在使用的主要参考是来自 John Farrier 的精彩演讲(时间戳在它显示如何舍入的时候):
https://youtu.be/k12BJGSc2Nc?t=1153
这种四舍五入对所有操作都非常有效,但却给了我这个部门的麻烦。 让我举个具体的例子。 我试图将645.68011474609375除以493.20962524414063
我得到的最终结果是:
我的:0-01111111-0100111100100011110000 0
c ++ _:0-01111111-0100111100100011110000 1
正如你可以看到除了最后一位之外的一切都匹配。我计算分部的方式是基于这个视频: https://www.youtube.com/watch?v=fi8A4zz1d-s
在此之后,我计算尾数精度24位(隐藏的一个+ 23尾数)和3位用于保护,圆形粘性加上额外的一个用于可能的移位。 使用视频算法,我最多可以得到1的归一化移位,这就是为什么我在末尾有一个额外的位,以防在规范化中移入,因此将在舍入中可用。现在这是我从除法算法得到的结果:
010100111100100011110000 0100
------------------------ ----
^ grs^
|__ to be normalized |____ extra bit
正如你所看到的,我在第24位得到0,所以我需要向左移动一个以获得正确的归一化。 这意味着我会得到:
10100111100100011110000 100
根据John Farrier的视频,在 100 grs位的情况下,如果尾数的LSB为1,我只会归一化。在我的情况下是零,这就是为什么我不会把结果弄清楚。
我有点失落的原因是我确信我的算法正在计算正确的尾数,我已经用在线计算器对其进行了双重检查,舍入策略适用于所有其他操作。此外,以这种方式计算会触发归一化,最终产生正确的指数。
我错过了什么吗?某个小细节?
有一件事让我感到奇怪的是粘性位,在加法和乘法中你会得到不同程度的移位,这会导致粘性位触发的可能性更高,在这种情况下,我只移动一个最大值,使粘性位不是真正的粘性。
我希望我提供足够的细节来解决我的问题。在这里你可以找到我的部门实现的底部,有点填充我用于调试的打印,但应该知道我在做什么,代码从第374行开始:
https://gist.github.com/giordi91/1388504fadcf94b3f6f42103dfd1f938
PS:同时我正在经历"科学家应该知道浮点数的所有内容"为了看看我是否遗漏了什么。答案 0 :(得分:7)
从分割算法得到的结果是不合适的。你表明:
010100111100100011110000 0100
------------------------ ----
^ grs^
|__ to be normalized |____ extra bit
数学上准确的商仍在继续:
010100111100100011110000 0100 110000111100100100011110…
因此,您正在舍入的点处的残留超过½ULP,因此应该向上舍入。我没有详细研究你的代码,但看起来你可能刚刚计算了一个或两个有效数字 1 。实际上你需要知道残差是非零的,而不仅仅是它的下一位或两位是零。如果在精确数学结果中该位置或之外的任何位非零,则最终粘滞位应为1。
1 “有意义”是首选术语。 “尾数”是对数的小数部分的遗留项。浮点值的有效位数是线性的。尾数是对数的。