为什么不添加这些花车?

时间:2015-11-25 18:56:11

标签: c++ windows floating-point

我知道这些浮点问题可能是堆栈溢出时最常问的问题,但我找不到看起来像我的东西。在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;
 }

1 个答案:

答案 0 :(得分:5)

经过一番调查后,我得出的结论是,您的代码可能会混淆floatdouble(或查看与您实际发布的内容不同的代码输出)。

这对我有用:

#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位精度。根据上面的评论,这似乎是一个特殊的软件产品,它实现了一些“魔术”,并且有一个简单的解决方法。