C == 运算符如何判断两个浮点值是否相等?

发布于 2024-11-13 11:41:41 字数 1080 浏览 2 评论 0原文

今天,我正在追查为什么我的程序在我编写的一些代码中遇到了一些意外的校验和不匹配错误,这些代码以包含 32 位校验和值(计算得出的格式)的格式序列化和反序列化 IEEE-754 浮点值通过对浮点数组的字节运行 CRC 类型算法)。

经过一番绞尽脑汁后,我意识到问题是 0.0f 和 -0.0f 具有不同的位模式(分别为 0x00000000 与 0x00000080(小端)),但 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;
}

Today I was tracking down why my program was getting some unexpected checksum-mismatch errors, in some code that I wrote that serializes and deserializes IEEE-754 floating-point values, in a format that includes a 32-bit checksum value (which is computed by running a CRC-type algorithm over the bytes of the floating-point array).

After a bit of head-scratching, I realized the problem was the 0.0f and -0.0f have different bit-patterns (0x00000000 vs 0x00000080 (little-endian), respectively), but they are considered equivalent by the C++ equality-operator. So, the checksum-mismatch errors happened because my checksum-calculating algorithm picked up the difference between those two bit-patterns, while certain other parts of my codebase (that use floating point equality testing, rather than looking at the values byte-by-byte) did not make that distinction.

Okay, fair enough -- I should probably have known better than to do floating-point equality testing anyway.

But this got me thinking, are there other IEEE-754 floating point values that are considered equal (according to the C == operator) but have different bit-patterns? Or, to put it another way, how exactly does the == operator decide whether two floating-point values are equal? Newbie me though it was doing something like memcmp() on their bit-patterns, but clearly it's more nuanced than that.

Here's a code example of what I mean, in case I wasn't clear above.

#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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

天气好吗我好吗 2024-11-20 11:41:41

它使用 IEEE-754 平等规则。

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

It uses the IEEE-754 equality rules.

  • -0 == +0
  • NaN != NaN
水染的天色ゝ 2024-11-20 11:41:41

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

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

 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< /code> ——因此相等测试将不起作用。

数值分析师对此深感担忧,但对于第一个近似值,定义一个值(APL 称之为 FUZZ)很有用,该值表示两个浮点数需要多接近才被视为相等。例如,您可以#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");

exact comparison. That's why it's best to avoid == as a test on floats. It can lead to unexpected and subtle bugs.

A standard example is this code:

 float f = 0.1f;

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

because 0.1 can't be represented precisely in binary (it's a repeating whatever the hell you call a fractional binary) 0.1*0.1 won't be exactly 0.01 -- and thus the equality test won't work.

Numerical analysts worry about this at length, but for a first approximation it's useful to define a value -- APL called it FUZZ -- which is how closely two floats need to come to be considered equal. So you might, for example, #define FUZZ 0.00001f and test

 float f = 0.1f;

 if(abs((f*f)-0.01f) < FUZZ)
     printf("0.1 squared is 0.01\n");
 else
     printf("Surprise!\n");
梦断已成空 2024-11-20 11:41:41

对于 Windows 平台,此链接具有 :

  • 除以 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
  • 当其中一个或两个操作数均为 NaN 时,比较 EQ、GT、GE、LT 和 LE 将返回 FALSE。
  • 比较忽略 0 的符号(因此 +0 等于 -0)。
  • 当一个或两个操作数均为 NaN 时,比较 NE 返回 TRUE。
  • 任何非 NaN 值与 +/- INF 的比较都会返回正确的结果。

For Windows platforms, this link has:

  • Divide by 0 produces +/- INF, except 0/0 which results in NaN.
  • log of (+/-) 0 produces -INF. log of a negative value (other than -0) produces NaN.
  • Reciprocal square root (rsq) or square root (sqrt) of a negative number produces NaN. The exception is -0; sqrt(-0) produces -0, and rsq(-0) produces -INF.
  • INF - INF = NaN
  • (+/-)INF / (+/-)INF = NaN
  • (+/-)INF * 0 = NaN
  • NaN (any OP) any-value = NaN
  • The comparisons EQ, GT, GE, LT, and LE, when either or both operands is NaN returns FALSE.
  • Comparisons ignore the sign of 0 (so +0 equals -0).
  • The comparison NE, when either or both operands is NaN returns TRUE.
  • Comparisons of any non-NaN value against +/- INF return the correct result.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文