通过整数添加浮点数与乘以浮点数的准确性

时间:2016-02-19 19:12:02

标签: floating-point precision floating-accuracy

在我的计算机科学课程中,我们正在研究浮点数以及它们在记忆中的表现方式。我已经理解它们在内存中的表示方式(尾数/有效数,指数及其偏差,以及符号位),我理解浮点数是如何相互相加和相减的(非规范化和所有有趣的东西)。然而,在查看一些研究问题时,我注意到了一些我无法解释的问题。

当一个无法精确表示的浮点数被多次添加到自身时,答案低于我们在数学上所期望的那样,但是当相同的浮点数乘以一个整数时,答案恰好出现在正确的数字上。

以下是我们研究问题的一个例子(该示例是用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得到精确而准确的值?

编辑为了澄清,在原始研究问题中,一些值被传递给函数,而其他值则在函数之外声明。我的示例代码缩短了,并简化了研究问题示例的版本。因为有些值传递给函数而不是显式定义为常量,所以我认为可以排除编译时的简化/优化。

2 个答案:

答案 0 :(得分:2)

首先,一些挑剔:

  

当浮动无法准确表示时

没有“不能精确代表的浮动”。所有float都可以精确地表示为float s。

  

被添加到自己好几次,答案比我们要低   数学上期望,

当你多次向自己添加一个数字时,你实际上可以获得比你想象的更高的东西。我将使用C99 hexfloat notation。考虑f = 0x1.000006p+0f。然后是f+f = 0x1.000006p+1ff+f+f = 0x1.800008p+1ff+f+f+f = 0x1.000006p+2ff+f+f+f+f = 0x1.400008p+2ff+f+f+f+f+f = 0x1.80000ap+2ff+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中,那么不要太惊讶,财务人员就像总和一样准确。