2011-06-07 21 views
7

今天,我被追查为什么我的计划是得到一些意想不到的校验和不匹配误差,在一些代码,我写的序列化和反序列化IEEE-754浮点值,在包括32位校验值的格式(通过在浮点数组的字节上运行CRC型算法来计算)。C ==运算符如何决定两个浮点值是否相等?

经过一番摸头之后,我意识到问题是0.0f和-0.0f分别具有不同的位模式(分别为0x00000000和0x00000080(little-endian)),但它们被认为是C++平等的运营商。所以,校验和不匹配错误发生是因为我的校验和计算算法找出了这两个位模式之间的差异,而我的代码库的某些其他部分(使用浮点相等性测试,而不是逐字节地查看值)字节)没有做出区分。

好了,很公平 - 我应该知道更好,而不是做浮点平等反正测试。

但是,这让我思考,是否有其他IEEE-754浮点被认为是相等的点值(根据到C ==操作符),但具有不同的位模式?或者,换句话说,==运算符究竟是如何决定两个浮点值是否相等?新手我虽然它做的像memcmp()他们的位模式,但显然它比这更细微。

这里是我的意思的代码示例,如果我没有上述澄清。

#include <stdio.h> 

static void PrintFloatBytes(const char * title, float f) 
{ 
    printf("Byte-representation of [%s] is: ", title); 
    const unsigned char * p = (const unsigned char *) &f; 
    for (int i=0; i<sizeof(f); i++) printf("%02x ", p[i]); 
    printf("\n"); 
} 

int main(int argc, char ** argv) 
{ 
    const float pzero = -0.0f; 
    const float nzero = +0.0f; 
    PrintFloatBytes("pzero", pzero); 
    PrintFloatBytes("nzero", nzero); 
    printf("Is pzero equal to nzero? %s\n", (pzero==nzero)?"Yes":"No"); 
    return 0; 
} 
+0

http://how-to.wikia.com/wiki/Howto_compare_floating_point_numbers_in_the_C_programming_language仅供参考,使用的ε是向前浮法比较的方式。不是在话题上,而是有用的知道。 – darvids0n 2011-06-07 01:10:08

+1

NaN的可能(可能取决于编译器)会有两种方式。由于存在大量可能的NaN(单精度为2^24-1),它们在内存中也可能不同。 – ughoavgfhw 2011-06-07 01:19:30

回答

13

它使用IEEE-754相等性规则。

  • -0 == +0
  • NaN != NaN
+2

+1 NaN。请注意,这是一种情况,位模式是* same *,但'=='会返回'false'。 – 2011-06-07 01:06:22

+1

+1'NaN'。还要注意,你可以有不同的位模式代表两个'NaN'。 – trutheality 2011-06-07 01:16:03

+0

的确,+0.0和-0.0是唯一可以比较相等的两种不同位模式。 – 2011-06-07 02:28:52

1

准确的比较。这就是为什么最好避免==作为浮点测试。它可能会导致意外和微妙的错误。

标准的例子是这样的代码:

float f = 0.1f; 

if((f*f) == 0.01f) 
    printf("0.1 squared is 0.01\n"); 
else 
    printf("Surprise!\n"); 

,因为0.1无法准确地以二进制(它重复不管你到底叫小数二进制)0.1*0.1不会完全0.01表示 - 因此平等测试将不起作用。

数值分析人员担心这一点,但对于第一个近似值来说,定义一个值非常有用--APL称之为FUZZ--这就是两个浮动物需要被认为是相等的。因此,您可能会举例如#define FUZZ 0.00001f和test

float f = 0.1f; 

if(abs((f*f)-0.01f) < FUZZ) 
    printf("0.1 squared is 0.01\n"); 
else 
    printf("Surprise!\n"); 
+6

显然,它不是按比特级别进行精确比较,或者它不会指示(-0.0f == 0.0f),因为这两个值具有不同的比特模式。 – 2011-06-07 01:02:09

+0

一个合理的点,这将是更贴切的了。我写“一点比较准确一点。” – 2011-06-07 01:11:47

2

对于Windows平台,this link has

  • 除以0产生+/- INF,除了0/0这导致为NaN。
  • 日志的(+/-)0产生-INF。负值(除-0以外)的日志产生NaN。
  • 倒数平方根(RSQ)或负数的平方根(SQRT)产生的NaN。例外是-0; sqrt(-0)产生-0,而rsq(-0)产生-INF。
  • INF - INF = NaN的
  • (+/-)INF /(+/-)INF = NaN的
  • (+/-)INF * 0 = NaN的
  • 的NaN(任何OP)的任何值= NaN的
  • 的比较EQ,GT,GE,LT,和LE,当任一或两个操作数为NaN返回FALSE。
  • 比较忽略的0符号(0,从而等于-0)。
  • 比较NE,当任一或两个操作数为NaN返回TRUE。针对+/- INF任何非NaN的值的
  • 比较返回正确的结果。