一份C++程序在两个IDE中返回不同结果

15

我在CodeBlocks中编写了以下的程序,结果为9183。然后我在Eclipse中编写同样的程序运行后返回9220。两个程序都使用了MinGW。正确结果应该是9183。这个代码有什么问题吗? 谢谢。

#include <iostream>
#include <set>
#include <cmath>

int main()
{
   using namespace std;
   set<double> set_1;
   for(int a = 2; a <= 100; a++)
   {
       for(int b = 2; b <= 100; b++)
       {
           set_1.insert(pow(double(a), b));
       }
   }
    cout << set_1.size();

return 0;
}

14
我猜测:你遇到了浮点精度错误。 - Chad
4
我不同意对这个问题的踩票。然而,这里相关的观点(也是我猜测的踩票原因)是你没有发布IDE编译使用的命令行参数。那些可能会有所不同。IDE本身只是不同工具的载体,它们大多是无关紧要的。 - Konrad Rudolph
我猜一个正在32位模式下编译,而另一个正在64位模式下编译,导致结果略有不同。 - Mat
3
如果这是downvotes的原因,那么没有任何评论的downvotes是毫无价值的,因为提问者不能改进他的问题。 - Olaf Dietsche
1
你可以打印中间结果来查看差异。 - SChepurin
显示剩余2条评论
4个回答

11

由于CodeBlocks编译成32位模式,而Eclipse编译成64位模式,所以您可能会看到精度误差:

$ g++ -m32 test.cpp
$ ./a.out
9183
$ g++ -m64 test.cpp
$ ./a.out
9220

那个比较是错误的。如果ab很小,那么边距会太大,如果ab很大,那么边距会太小。 - Dietrich Epp
1
@DietrichEpp:你可能是对的;由于我对C++中浮点数比较的知识不足,我将删除该方程式,并留给读者作为练习去找到使用双精度浮点数作为“set”键的正确方法(提示:不要这样做)。 - Justin ᚅᚔᚈᚄᚒᚔ
3
使用 == 比较浮点数并不一定是错误的,有很多情况下这样做是正确的。但需要注意的是,用两种不同的方式计算相同的数字可能会得到稍微不同的结果,例如 exp(2.0)square(exp(1.0)),而不同的实现也会给出不同的结果。因此,如果你确实知道自己在做什么,使用 set<double> 可能是可以的。 - Dietrich Epp
2
为了后人留存:关于将浮点数作为集合键的相关讨论,请参见此处:https://dev59.com/iGw15IYBdhLWcg3wVKJ1。 - Justin ᚅᚔᚈᚄᚒᚔ
容器的大小与双精度浮点数、浮点数或字符有什么关系? - Thomas Matthews
显示剩余2条评论

3

如果我将两个参数转换为双精度浮点数,我会得到预期的结果:

pow(static_cast<double>(a), static_cast<double>(b))

3

这个差异似乎是由浮点运算使用53位精度还是64位精度造成的。如果在循环前面添加以下两行代码(假设是Intel架构),它将使用53位精度,并在编译为32位应用程序时给出9220的结果:

uint16_t precision = 0x27f;
asm("fldcw %0" : : "m" (*&precision));

这个精度取决于FPU的第8位和第9位。上述设置了这两个位为10。将它们设置为11可以得到64位精度。此外,如果将这些位设置为00(值为0x7f),则大小将被打印为9230


2

实际上,对于双精度浮点数,你并不应该依赖于==(或技术上讲,x <= y && y <= x)。因此,这段代码会产生与实现相关的结果(严格来说不是UB,根据评论,但我的意思是 :))


5
这不是未定义行为——对双精度数进行比较不会导致崩溃或破坏内存——但不同的实现可能会影响操作的精确度,因此一个编译器产生的结果可能与另一个有所不同。 - Chris Dodd
我不明白你的回答是什么意思。我从一个IDE复制了代码,然后粘贴到另一个IDE中。 - Sam
2
编译器可以自由地对双精度值进行一些舍入。例如,4 可能被存储为 3.000019,而 2 则被存储为 1.99998,因此 2.0*2.0 != 4.0。但是,如果编译器在这些情况下是精确的,因为它被允许这样做,它们将相等。这就是你在集合中所依赖的东西。 - djechlin
由于两个IDE都使用MinGW,我期望得到相同的结果,无论是9183还是9220。 - Olaf Dietsche
他们都使用gcc吗?很明显,它们产生了不同的汇编代码-比较每个编译阶段的输出。 - djechlin
2
@djechlin 不,编译器并不完全“自由”这样做。编译器被迫遵守相关的IEEE标准,这些行为被非常精确地规定了。余地很小。(然而,编译器可以做的是,在重新排序表达式以假设这种重新排序没有可观察到的效果时,忽略这种行为。) - Konrad Rudolph

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接