将双精度数与双精度字面量进行比较?

4

可能是重复问题:
如何进行浮点数比较?

在C++中,不建议将double和double字���量进行相等性比较,因为我猜这取决于编译器?

更准确地说,不能比较硬编码的double(源代码中的文字)和应计算的double,因为计算结果的最后一个数字可能因编译器而异。这是否有标准化规定?

我听说在Knuth的TeXbook中提到了这一点,是真的吗?

如果以上都是正确的,解决方案是什么?


1
我从未听说过这个... - Tony The Lion
Knuth的书里面没有C++内容。 - R. Martinho Fernandes
1
当你说“compare”时,你是否是指“相等性”?我无法想象在其他情况下会出现问题。 - Marcus Riemer
4个回答

6
您有一些误解这条建议。关键在于浮点数计算并不是精确的,会出现舍入误差和逐渐丢失精度的情况。以简单的1.0/10.0为例,结果本应该是0.1,但实际上不是,因为0.1无法在浮点格式中精确表示。因此,实际结果会略有不同。对于无数其他操作也是如此,所以这个观点与const doubles无关,而是与不期望结果精确有关。如果您执行某些计算,其中结果应该是1.0,那么您不应该将其与1.0进行相等测试,因为舍入误差可能意味着它实际上是0.9999999997
因此,通常的解决方案是测试结果是否足够接近1.0。如果足够接近,我们就假设“它已经足够好了”,并将其视为结果为1.0。
总之,严格相等很少用于浮点值。相反,您应该测试两个值之间的差异是否小于某个小值(通常称为epsilon)。

1
对于误导性的“只测试某个绝对误差”,我更倾向于模糊但足够接近的+1。 - R. Martinho Fernandes
这里出现了两个问题:编译器是否需要标准化舍入数字?在C++中将0.1存储为double的问题是什么? - Narek
@Narek:0.1在二进制中是一个无限扩展的数字,有点像你无法用十进制表示1/3。 - R. Martinho Fernandes
@Narek:第二部分相当容易理解。并非所有的分数都可以在每个数字基数中表示。例如,在十进制中,你无法精确地表示 1/3。你必须在某个点上四舍五入,并且失去精度(0.333333...)。同样,在二进制中(浮点数和双精度数使用的基数),你也无法精确地表示 1/10 - jalf
我不太确定你在第一部分中的意思。 - jalf

1
你所提到的问题是由于舍入误差引起的,对于每个浮点数都会发生。你可以定义一个epsilon并查看两个浮点数之间的差异是否小于它。例如:
double A = somethingA();
double B = somethingB();

double epsilon = 0.00001;

if (abs(A - B) < epsilon)
  doublesAreEqual();

[编辑] 还可以参考这个问题:浮点数比较的最有效方法是什么?


1
不要盲目地用这个解决你的问题,好吗? - R. Martinho Fernandes
请注意,标准库提供了 std::numeric_limits<T>::epsilon() 函数来确定类型 T 可以表示的最小可能与 1.0 的差异;但是不要将其用于上述检查(因为 epsilon 对于更高的指数将 可表示),仅供参考。 - sehe
@Martinho:这让我们都非常好奇您的答案是什么?如果您想警告不要使用“任意”的固定epsilon,为什么不直接说明呢?这样您的评论会更有帮助。 - sehe
1
@sehe:简而言之,因为解决方案取决于确切的问题。这只是解决从FP的本质中产生的一部分问题的解决方案。我喜欢这个答案 - R. Martinho Fernandes
@sehe:我认为很明显他担心OP只是把这段代码扔给问题,而不停下来仔细思考情况以及如何将_思维方式_而非_代码示例_应用到他的程序及未来编写的程序中。由于过去曾经多次向人们解释过浮点数的不准确性,我当然能理解他的感受。 - Lightness Races in Orbit
显示剩余3条评论

0

关键问题在于浮点数算术运算的工作方式 - 它包括四舍五入,可能导致比较相等计算错误。这适用于所有浮点数,无论变量是否声明为const


0
如果您进行浮点数计算并需要与某些固定值进行比较,则始终最好使用一个epsilon值来考虑精度误差。
例如:
double calcSomeStuf();

if ( calcSomeStuf() == 0.1 ) { ...}

这是一个不好的想法。

然而:

const double epsilon = 0.005

double calcSomeStuf();

if ( abs(calcSomeStuf() - 0.1) < epsilon ) { ...}

这样做更安全(特别是考虑到0.1无法准确表示为双精度)

这是必要的,因为在累加浮点运算时会出现舍入误差,并且由于浮点数的本质,不是所有数字都可以准确表示。


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