浮点数中同二进制NaN不相等但Inf相等的规则差异原因及C语言实现问询
浮点数中同二进制NaN不相等但Inf相等的规则差异原因及C语言实现问询
这问题问得特别戳中浮点数的核心细节,其实背后全是IEEE 754浮点数标准在起作用,C语言只是老老实实遵循了这个工业界通用的标准而已,咱们一步步掰扯清楚:
一、先搞懂IEEE 754对Inf和NaN的核心规则
IEEE 754是所有现代计算机遵循的浮点数标准,它直接定义了这些特殊值的比较逻辑:
- 无穷大(Inf):当浮点数的指数位全为1、尾数位全为0时,就表示无穷大(符号位区分正负)。标准明确规定:同符号的无穷大是相等的。所以只要两个Inf的二进制完全一致(符号、指数、尾数位都相同),用
==比较就会返回真。 - 非数(NaN):当指数位全为1、尾数位不全为0时,就是NaN(代表无效计算的结果,比如0/0、√-1)。标准强制规定:任何NaN与任何值(包括它自己)的
==比较结果都是假。哪怕两个NaN的二进制位完全一模一样,也不能用==判定为相等——因为NaN的本质是“不是一个有效的数”,标准认为“无效数之间没法定义相等性”。
二、C语言里的实现逻辑
C语言本身并没有自己定义浮点数的比较规则,而是把这个活儿交给了底层的硬件(CPU的浮点数运算单元)或者标准库,而这些底层实现全都是严格遵循IEEE 754的:
- 当你用
==比较两个浮点数时,CPU的浮点数比较电路会先检查操作数是不是NaN:只要其中一个是NaN,直接返回假(只有!=比较NaN时会返回真)。 - 而你写的位wise比较是直接对比内存里的二进制字节,完全绕过了浮点数的比较规则——不管是Inf还是NaN,只要内存里的每一位都一样,位异或
^的结果就是0,自然会判定为“相等”,这是内存层面的完全一致,和浮点数的语义规则没关系。
三、结合你写的代码验证
第一个Inf的代码示例
#include <stdio.h> int main(){ unsigned char bin1[] = {0b00000000, 0b00000000, 0b00000000, 0b0000000, 0b00000000, 0b0000000, 0b11110000, 0b01111111}; unsigned char bin2[] = {0b00000000, 0b00000000, 0b00000000, 0b0000000, 0b00000000, 0b0000000, 0b11110000, 0b01111111}; double num1 = *(double *)bin1; double num2 = *(double *)bin2; printf("num1 : %lf\nnum2 : %lf\n\n", num1, num2); if (num1 == num2) printf("num1 == num2 (built in)\n"); else printf("num1 != num2 (built in)\n"); int con = 0; for (int i = 0; i < 8; i++) { if (!(bin1[i] ^ bin2[i])) con = 1; break; } if (con) printf("num1 == num2 (bitwise)\n"); else printf("num1 != num2 (bitwise)\n"); }
这里的bin1和bin2是完全一致的正无穷大(double类型):
- 用
==比较时,符合IEEE 754的同符号Inf相等规则,所以输出num1 == num2 (built in) - 位比较时,二进制完全一致,所以也输出相等,这和浮点数规则不冲突。
第二个NaN的代码示例
#include <stdio.h> int main(){ unsigned char bin1[] = {0b00000001, 0b00000000, 0b00000000, 0b0000000, 0b00000000, 0b0000000, 0b11110000, 0b01111111}; unsigned char bin2[] = {0b00000001, 0b00000000, 0b00000000, 0b0000000, 0b00000000, 0b0000000, 0b11110000, 0b01111111}; double num1 = *(double *)bin1; double num2 = *(double *)bin2; printf("num1 : %lf\nnum2 : %lf\n\n", num1, num2); if (num1 == num2) printf("num1 == num2 (built in)\n"); else printf("num1 != num2 (built in)\n"); int con = 0; for (int i = 0; i < 8; i++) { if (!(bin1[i] ^ bin2[i])) con = 1; break; } if (con) printf("num1 == num2 (bitwise)\n"); else printf("num1 != num2 (bitwise)\n"); }
这里的bin1和bin2是完全一致的NaN(尾数位不全为0):
- 用
==比较时,触发IEEE 754的NaN规则,直接返回假,所以输出num1 != num2 (built in) - 位比较时,内存二进制完全一致,所以输出
num1 == num2 (bitwise),这是内存层面的一致,和浮点数的语义规则无关。
最后补个小知识点
如果你在C里想判断一个值是不是NaN,不能用x == NaN(因为永远返回假),得用标准库的isnan()函数(需要包含<math.h>),这个函数会直接检查浮点数的二进制格式,判断是不是NaN。
核心逻辑就是IEEE 754的标准规定,C只是遵循了这个标准而已~




