【发布时间】:2012-11-26 06:39:48
【问题描述】:
一个常见的假设是1 / x * x == 1。在常见的符合 IEEE 754 的硬件上打破这一点的最小正整数是多少?
当乘法逆的假设失败时,写得不好的有理算术就不再起作用了。因为包括 C 和 C++ 在内的许多语言默认使用舍入到零将浮点数转换为整数,所以即使是很小的错误也可能导致整数结果偏位。
快速测试程序会产生各种结果。
#include <iostream>
int main () {
{
double n;
for ( n = 2; 1 / n * n == 1; ++ n ) ;
std::cout << n << " (" << 1 - 1/n*n << ")\n";
for ( ; (int) ( 1 / n * n ) == 1; ++ n ) ;
std::cout << n << " (" << 1 - 1/n*n << ")\n";
}
{
float n;
for ( n = 2; 1 / n * n == 1; ++ n ) ;
std::cout << n << " (" << 1 - 1/n*n << ")\n";
for ( ; (int) ( 1 / n * n ) == 1; ++ n ) ;
std::cout << n << " (" << 1 - 1/n*n << ")\n";
}
}
在 ideone.com 上使用 GCC 4.3.4 结果是
41 (5.42101e-20)
45 (5.42101e-20)
41 (5.42101e-20)
45 (5.42101e-20)
使用 GCC 4.5.1 会产生相同的结果,但报告的误差范围恰好为零。
在我的机器上(GCC 4.7.2 或 Clang 4.1),结果是
49 (1.11022e-16)
49 (1.11022e-16)
41 (5.96046e-08)
41 (5.96046e-08)
这与--fast-math 选项无关。使用-mfpmath=387 出乎意料地产生了
41 (5.42101e-20)
41 (5.42101e-20)
41 (5.42101e-20)
41 (5.42101e-20)
值 5×10-20 似乎暗示 epsilon 对应于 64 位尾数,即使用 Intel 80 位扩展精度的内部计算。
这似乎高度依赖于 FPU 硬件。是否有适合测试的可靠值?
注意:我不在乎语言标准或编译器对浮点数系统的保证,尽管我认为在任何常见的编程系统中都没有很多有意义的保证。我想知道数字和现实世界计算机之间的交互。
【问题讨论】:
-
它可能早在
1/3 * 3就失败,因为1/3不能用二进制浮点数精确表示。事实证明准确的唯一方法是,如果1/3 * 3恰好朝1舍入,而不是0.99999...或1.00000001之类的。 -
@Mysticial 可以,但通常不会。似乎 FPU 的设计目的是不这样做。我想知道可靠失败的最低值是多少。或者,FPU 采用什么二进制技巧能够将数字正确四舍五入到 40,但仍然在该范围内的不同点处失败。
-
我很想投票结束这个问题,因为它没有说明浮点错误或推理浮点运算的好方法。具体询问
1/x*x==1失败的条件或存在 x 的 r 使得x*r==1评估为 true 提供的关于浮点如何工作的见解很少,并且为预测或控制任何其他情况下的错误提供很少的基础。此外,该问题忽略了语言标准或编译器,但尝试使用语言和编译器来实验性地调查问题。 -
对浮点属性的认识非常低;像这样的问题可能会帮助程序员更多地注意真正的陷阱,这就是我投赞成票的原因。
-
@BrianDrummond:如果您了解了这个问题的答案,那么这对任何其他浮点问题有什么帮助?我们得到的答案并没有解释除法中的舍入和随后的乘法如何结合产生 1 或不产生 1。它们没有详细说明浮点格式或如何计算误差范围。关于哪个 x 是具有此错误的最小整数没有什么意义。这只是一个随机的问题,与其他任何事情几乎没有关系。这不是设计浮点计算的人使用浮点的方式。
标签: language-agnostic floating-point floating-accuracy ieee-754