在C中,在使用IEEE-754浮点数的实现中,当我比较两个NaN的浮点数时,它返回0或"false"。 但是为什么两个浮点数都被认为是相等的呢?
这个程序打印"相等:......"(至少在Linux AMD64下使用gcc),在我看来它应该打印"不同:......"。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <stdio.h>
#include <stdlib.h>
int main (void)
{
volatile double a = 1e200; //use volatile to suppress compiler warnings
volatile double b = 3e200;
volatile double c = 1e200;
double resA = a * c ; //resA and resB should by inf
double resB = b * c ;
if (resA == resB )
{
printf("equal: %e * %e = %e = %e = %e * %e
",a ,c ,resA ,resB ,b ,c );
}
else
{
printf("different: %e * %e = %e != %e = %e * %e
", a , c , resA , resB , b , c );
}
return EXIT_SUCCESS ;
} |
另一个例子,为什么我认为inf与inf不同,是:自然数和有理数的数量,两者都是无限的但不相同。
那么为什么inf == inf?
-
考虑这个代码并不一定比较2 double两者都是infinity。 此代码比较2个操作的结果是否相等。 C允许中间计算使用更宽的类型进行操作。 在那种情况下,我希望a*c==b*c是假的。 更直接的示例代码将使用无穷大的volatile double对象然后进行比较。
-
@chux谢谢,我编辑源代码
无限比较相等,因为这是标准所说的。从5.11节比较谓词的细节:
Infinite operands of the same sign shall compare equal.
-
谢谢,但为什么标准说呢?
-
@ 12431234123412341234123这是一个不同的问题。
-
@nwellnof,但那正是我试图问的问题。
inf==inf出于同样的原因,几乎所有浮点数都与自己相等:因为它们是相等的。它们包含相同的符号,指数和尾数。
你可能在考虑如何NaN != NaN。但这对于更重要的不变量来说是一个相对不重要的结果:NaN != x对于任何x。顾名思义,NaN根本不是任何数字,因此无法比较等于任何数字,因为所讨论的比较是数字比较(因此-0 == +0)。
将inf与其他inf进行比较不等于肯定会有一些意义,因为在数学语境中它们几乎肯定是不相等的。但请记住,浮点平等与绝对数学平等不同; 0.1f * 10.0f != 1.0f,1e100f + 1.0f == 1e100f。正如浮点数逐渐下降到非正规数而不会损害尽可能好的平等,所以它们会溢出到无穷大而不会损害尽可能好的平等。
如果需要inf != inf,则可以模拟它:1e400 == 3e400计算结果为true,但1e400 - 3e400 == 0计算结果为false,因为+inf + -inf的结果为NaN。 (可以说你可以说它应该评估为0,但这对任何人都没有兴趣。)
-
"根本没有任何数字"inf也不是数字。
-
"无限"概念不是实数系统中的数字。但inf IEEE-754值确实是IEEE-754中的一个数字。
-
FWIW,C规范使用浮点类型(float,double,...):标准化浮点数,次正规浮点数,非标准化浮点数和非浮点数值,如无穷大和NaNs。
-
@chux好点。我应该说,inf远远超过NaN。
-
@chux虽然我进一步指出他们将"浮点数"定义为"以浮点格式表示的有限或无限数"。 (如何以浮点格式表示任何"无限数"......对于哲学家来说是一个问题。)
-
(754,即不是C.)
背景
在C中,根据IEEE 754二进制浮点标准(因此,如果使用float或double),您将得到一个精确值,可以与同一类型的另一个变量进行精确比较。嗯,这是正确的,除非你的计算产生的值超出了可以表示的整数范围(即溢出)。
为什么Infinity ==无限
resA和resB
IEEE-754标准将无穷大和负无穷大的值分别大于或小于可以根据标准(<= INFINITY == 0 11111111111 0000000000000000000000000000000000000000000000000000和>= -INFINITY == 1 11111111111 0000000000000000000000000000000000000000000000000000)表示的所有其他值,除了NaN,这是不小于,等于或大于任何浮点值(甚至本身)。请注意,无穷大和它的负数在其符号,指数和尾数位中都有明确的定义。
因此,resA和resB是无穷大的,因为无穷大是明确定义和可再现的,resA==resB。我很确定这是isinf()的实现方式。
为什么NaN!= NaN
但是,没有明确定义NaN。 NaN值的符号位为0,所有1的指数位(就像无穷大而且它是负的),以及任何非零分数位(源)。那么,如果他们的分数位是任意的,你怎么能告诉另一个NaN呢?好吧,标准不假设并且当这个结构的两个浮点值相互比较时简单地返回false。
更多解释
因为无穷大是一个明确定义的值(Source,GNU C Manual):
Infinities propagate through calculations as one would expect
2 + ∞ = ∞
4 ÷ ∞ = 0
arctan (∞) = π/2.
但是,NaN可能通过计算传播也可能不通过传播传播。当它发生时,它是一个QNan(Quieting NaN,最重要的分数位集),所有计算都将导致NaN。当它没有时,它是一个SNan(信令NaN,未设置的最重要的分数位),并且所有计算都将导致错误。
-
NAN=0x7ff0000000000001(以及其他值),但NAN != NAN。并且在浮点上下文中0x8000000000000000 == 0x0000000000000000。它并不像比较比特那么简单(虽然它几乎就像那样简单)。
-
哦,它没有。我只是指出"特殊"值的相等性,如inf和NaN和-0,取决于IEEE委员会做出的个别决定,而不是硬性和快速的数学规则。 (好吧,除了-0==0,这非常重要,但是-0的存在不是。)如果他们走了另一条路并且做了inf != inf,那么没有飞机会掉出天空。
-
@Sneftel这是一个公平的观点,只是想确保我明白你的来源。至于IEEE委员会决定与严格的数学规则之间的差异,其中一位委员会成员在此发表了一篇非常长篇的文章。
-
刚看到你早先的附录。似乎不可能任何编译器在没有volatile的情况下产生警告,但不会产生一个带有一个(在本地变量上volatile,其地址永远不会被应用register),但它完全有可能我错了。
-
声明对象volatile当然是为了防止优化没有发生像最初发布的更宽的数学(long double) - 但没有。通过将无穷大指定为resA, resB,该问题是侧面的。
-
@chux我不确定如何在编译原始发布时验证这些优化是否发生。你说的是为了执行a*b或b*c,C99编译器会像long double那样重新解释a,b和c?
-
一个完全兼容的C99编译器不应该,但已知现实世界的编译器将扩??展精度寄存器中的值保持比技术上应该更长的时间。 volatile是将代码生成器打造成形状的一种潜在工具。 (但我不认为这种情况很重要,因为AFAIK扩展精度FPU不扩展指数,只扩展尾数。)
-
@VladislavMartin a,b,....将被视为double。产品a*c的计算可以通过首先推广到long double并乘以来完成。原始a*c==b*c可能是long double比较。 OP已编辑为double resA = a*c;,强制将reaA, reaB检索为double。代码比较== double或long double不是问题,因为代码正在比较无穷大和无穷大。因此,我们不需要验证由于reaA, reaB 分配而未发生优化。 IAC,现在没有实际意义。
有许多算术系统。 其中一些,包括高中数学中通常涵盖的那些,例如实数,没有无穷大的数字。 其他人有一个无穷大,例如投射扩展的实线。 其他的,例如正在讨论的IEEE浮点运算,以及扩展的实线,具有正无穷大和负无穷大。
IEEE754算法在许多方面与实数算术不同,但对于许多目的而言是有用的近似。
NaNs和无穷大的不同处理有逻辑。 说正无穷大大于负无穷大和任何有限数是完全合理的。 对-1的平方根说任何类似的东西都是不合理的。