我知道这些浮点问题可能是堆栈溢出时最常问的问题,但我找不到看起来像我的东西。在windows(visual studio)中编译为32位,如果我这样做:
double lnA = 1448481410.0;
double lnB = 0.75599998235702515;
double lnC = lnA + lnB;
我得到lnC = 1448481408.0000000。由于浮点表示,我可以理解一个小的差异,但我不明白为什么lnA - lnC == 2?
更新: 所以这是使用Visual Studio 2010的实际输出:这是一个MFC应用程序,这就是我使用TRACE的原因。
double lnA = 1448481410.0;
double lnB = 0.75599998235702515;
double lnC = lnA + lnB;
TRACE("A = %f B = %f C = %f A - C = %f\n",lnA, lnB, lnC, lnA - lnC);
A = 1448481410.000000 B = 0.756000 C = 1448481408.000000 A - C = 2.000000
UPDATE2:在尝试制作如下所示的最小完整示例时,我没有看到同样的问题。只有当它是我的大型应用程序的一部分。有什么想法吗?
#include <iostream>
int main() {
double lnA = 1448481410.0;
double lnB = 0.75599998235702515;
double lnC = lnA + lnB;
std::cout << "A: " << lnA << "B: " << lnB << "C: " << "Diff: " << lnA - lnC << std::endl;
return 0;
}
答案 0 :(得分:5)
经过一番调查后,我得出的结论是,您的代码可能会混淆float
和double
(或查看与您实际发布的内容不同的代码输出)。
这对我有用:
#include <iostream>
int main()
{
double lnA = 1448481410.0;
double lnB = 0.75599998235702515;
double lnC = lnA + lnB;
std::cout << std::fixed << "A:" << lnA << " B:" << lnB << " C:" << lnC << std::endl;
}
产地:
$ ./a.out
A:1448481410.000000 B:0.756000 C:1448481410.756000
现在,正如Soulsabr在评论中所说,如果我们使用float
代替double
,结果会有所不同:
只有这些行改变了:
float lnA = 1448481410.0;
float lnB = 0.75599998235702515;
float lnC = lnA + lnB;
$ ./a.out
A:1448481408.000000 B:0.756000 C:1448481408.000000
这是因为在典型系统中,float
中的位数是32位,它被分为8位指数,1位符号和23位[加上一个隐藏]尾数。因此值为S * M * 2^E
,其中S
为符号,M
为尾数,E
为指数。 M
的大小为23位,因此可用于精确描述高达约8百万的值。我们可以使用E来移动值,但无论我们选择什么值,在数字内可以改变的最小值是x / 8百万实际值的下一个更大的整数。因此,“有所作为”的最小值中有1400万变为+/- 2。添加1或更少将完全没有效果。
double
代码“有效”,因为64位双尾有一个53位尾数,它允许该值为实际值的+/- 1/2 ^ 53,这是一个更大的值,并允许更精确的计算。但要取得足够大和足够小的价值,如果它们足够远,我们就会遇到同样的问题。这只是浮点值如何工作的问题。你只有这么多位。有“大数学”库允许更多位,(“无限”,受内存可用),但当然,值越大,计算速度越低,对于大多数事情,1/2 ^ 53价值“足够好”。
编辑(根据OP的评论):
当使用x87指令时,FPU被设置为“舍入到32位”,这意味着即使使用64位浮点值进行计算,也会发生与“使用浮点数”类似的效果,中间结果舍入为32位精度。根据上面的评论,这似乎是一个特殊的软件产品,它实现了一些“魔术”,并且有一个简单的解决方法。