【发布时间】:2016-06-01 11:43:21
【问题描述】:
在我的计算机科学课程中,我们正在研究浮点数以及它们在内存中的表示方式。我已经了解它们在内存中的表示方式(尾数/有效数、指数及其偏差以及符号位),并且我了解浮点数是如何相互相加和相减的(非规范化和所有有趣的东西)。然而,在查看一些学习问题时,我发现了一些我无法解释的东西。
当一个无法精确表示的浮点数被多次添加到自身时,答案会低于我们在数学上的预期,但是当同一个浮点数乘以一个整数时,答案就会精确到正确的数字。
这是我们学习问题中的一个示例(该示例是用 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,p 的最后一个值与max 的值之间的差值为-9.536743E-7。从逻辑的角度来看(知道浮点数是如何工作的),这个值是有意义的。
不过,我不明白的是,为什么我们在第一个示例中正好得到 10.0。从数学上讲,我们得到 10.0 是有道理的,但是知道浮点数是如何存储在内存中的,这对我来说没有意义。谁能解释为什么我们通过将不精确的浮点数与 int 相乘来得到精确的值?
编辑:为了澄清,在最初的研究问题中,一些值被传递给函数,而其他值被声明在函数之外。我的示例代码是研究问题示例的缩短和简化版本。因为有些值是传入函数而不是显式定义为常量,所以我相信可以排除编译时的简化/优化。
【问题讨论】:
-
因为编译器将所有这些都减少为一个常量值。尝试将每个语句都设为函数,然后一个接一个地调用。
-
@Amit,我很抱歉,我应该在我的问题中说清楚。示例中定义的一些值作为变量传递给计算最终结果的函数,因此它似乎不太可能是编译器优化。我试图简化这篇文章的代码,所以我在示例中定义了值。我会尽快进行编辑以澄清这一点。
-
除非您的编辑让我感到惊讶,否则我的评论(如果您愿意,我会将其作为答案发布)仍然有效。编译器会将所有语句优化为
max值,因为所有语句都会进行来回计算。 -
他可以通过在命令行或文件中输入数字来排除这种情况,因此它们是变量而不是编译时间常数。
-
我确定他们想教您的是浮点数已损坏且需要小心,因为您无法以 2 为基数的浮点格式精确表示小数。重点是避免 10 次加法和 1 次乘法以提高精度。
标签: floating-point precision floating-accuracy