今天我正在追踪我的程序为什么会出现一些意外的校验和错误错误,在我编写的一些代码中,这些代码序列化和反序列化IEEE-754浮点值,格式包含32位校验和值(这是通过在浮点数组的字节上运行CRC类型算法来计算的。
经过一番搔痒之后,我意识到问题是0.0f和-0.0f分别有不同的位模式(0x00000000 vs 0x00000080(little-endian)),但它们被认为是C ++相等的等价物 - 运算符。因此,校验和不匹配错误的发生是因为我的校验和计算算法选择了这两个位模式之间的差异,而我的代码库中的某些其他部分(使用浮点相等测试,而不是逐字节地查看值) byte)没有做出这种区分。
好的,足够公平 - 无论如何,我应该知道比进行浮点平等测试更好。
但这让我想到,是否有其他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 :(得分:13)
它使用IEEE-754相等规则。
-0 == +0
NaN != NaN
答案 1 :(得分: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。
- 比较任何非NaN值与+/- INF将返回正确的结果。
答案 2 :(得分: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
- 因此等式测试赢得了'工作。
数值分析师长期担心这一点,但是对于第一次近似,定义一个值是有用的 - 称为FUZZ的APL - 两个浮点数需要多么接近才能被认为是相等的。例如,您可能会#define FUZZ 0.00001f
和测试
float f = 0.1f;
if(abs((f*f)-0.01f) < FUZZ)
printf("0.1 squared is 0.01\n");
else
printf("Surprise!\n");