在我的计算机科学课程中,我们正在研究浮点数以及它们在记忆中的表现方式。我已经理解它们在内存中的表示方式(尾数/有效数,指数及其偏差,以及符号位),我理解浮点数是如何相互相加和相减的(非规范化和所有有趣的东西)。然而,在查看一些研究问题时,我注意到了一些我无法解释的问题。
当一个无法精确表示的浮点数被多次添加到自身时,答案低于我们在数学上所期望的那样,但是当相同的浮点数乘以一个整数时,答案恰好出现在正确的数字上。
以下是我们研究问题的一个例子(该示例是用Java编写的,为简单起见,我编辑了它):
float max = 10.0f; /* Defined outside the function in the original code */
float min = 1.0f; /* Defined outside the function in the original code */
int count = 10; /* Passed to the function in the original code */
float width = (max - min) / count;
float p = min + (width * count);
在这个例子中,我们被告知结果恰好是10.0
。但是,如果我们将这个问题看作浮点数的总和,我们得到的结果略有不同:
float max = 10.0f; /* Defined outside the function in the original code */
float min = 1.0f; /* Defined outside the function in the original code */
int count = 10; /* Passed to the function in the original code */
float width = (max - min) / count;
for(float p=min; p <= max; p += width){
System.out.printf("%f%n", p);
}
我们被告知此测试中p
的最终值为~9.999999
,-9.536743E-7
的最后一个值与{{}的值之间的差异为p
1}}。从逻辑的角度来看(知道浮点数是如何工作的),这个值是有意义的。
我不明白的是,为什么我们在第一个例子中得到10.0。从数学角度来说,我们得到10.0是有意义的,但是知道浮点数是如何存储在内存中的,这对我来说没有意义。任何人都可以解释为什么我们通过将不精确的浮点数乘以int得到精确而准确的值?
编辑:为了澄清,在原始研究问题中,一些值被传递给函数,而其他值则在函数之外声明。我的示例代码缩短了,并简化了研究问题示例的版本。因为有些值传递给函数而不是显式定义为常量,所以我认为可以排除编译时的简化/优化。
答案 0 :(得分:2)
首先,一些挑剔:
当浮动无法准确表示时
没有“不能精确代表的浮动”。所有float
都可以精确地表示为float
s。
被添加到自己好几次,答案比我们要低 数学上期望,
当你多次向自己添加一个数字时,你实际上可以获得比你想象的更高的东西。我将使用C99 hexfloat notation。考虑f = 0x1.000006p+0f
。然后是f+f = 0x1.000006p+1f
,f+f+f = 0x1.800008p+1f
,f+f+f+f = 0x1.000006p+2f
,f+f+f+f+f = 0x1.400008p+2f
,f+f+f+f+f+f = 0x1.80000ap+2f
和f+f+f+f+f+f+f = 0x1.c0000cp+2f
。但是,7.0*f = 0x1.c0000a8p+2
,其轮次为0x1.c0000ap+2f
,小于f+f+f+f+f+f+f
。
但是当相同的float乘以整数时,答案是, 恰好是正确的数字。
7 * 0x1.000006p+0f
无法表示为IEEE float
。因此它变得圆润。使用round-to-nearest-with-ties-even-even的默认舍入模式,当你执行这样的单个算术运算时,你得到最接近你的确切结果的浮点数。
但我不明白的是,为什么我们得到10.0 对于第一个例子。在数学上,我们有意义 得到10.0,但知道浮点数如何存储在内存中,它没有 对我有意义。任何人都可以解释为什么我们得到精确和准确 通过将不精确的浮点数与int?
相乘来得到的值
要回答您的问题,您会得到不同的结果,因为您执行了不同的操作。你在这里得到了“正确”的答案,这有点侥幸。
让我们切换数字。如果我计算0x1.800002p+0f / 3
,我会获得0x1.00000155555...p-1
,其转向0x1.000002p-1f
。当我把它变为三倍时,我得到0x1.800003p+0f
,其中(因为我们断开与偶数的关系)到0x1.800004p+0f
。如果我在f+f+f
算术float
中计算f = 0x1.000002p-1f
,这与我得到的结果相同。
答案 1 :(得分:1)
因为1.0 + ((10.0 - 1.0) / 10.0) * 10.0
只进行了1次不精确值的计算,因此有1次舍入误差,所以比10次增加0.9f的float表示更准确。我认为这是本例中要教授的校长。
关键问题是0.1不能用浮点表示。 所以0.9中有错误,它在函数循环中加起来。
&#34;确切&#34;数字,可能是因为巧妙的输出格式化例程而显示的。当我第一次使用计算机时,他们喜欢用荒谬的科学固定数字格式输出这些数字,这种格式对人类不友好。
我想要明白发生了什么我会找到Koenig的Dobbs博士关于这个主题的博客文章,这是一个启发性的阅读,该系列通过展示如何使用语言来达到顶峰perl,python&amp;如果它们足够精确,可能java会使计算看起来很精确。
Even Simple Floating-Point Output Is Complicated
{{3}}
如果固定点算法在5到10年后被添加到CPU中,那么不要太惊讶,财务人员就像总和一样准确。