You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

浮点数中同二进制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");
}

这里的bin1bin2是完全一致的正无穷大(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");
}

这里的bin1bin2是完全一致的NaN(尾数位不全为0):

  • ==比较时,触发IEEE 754的NaN规则,直接返回假,所以输出num1 != num2 (built in)
  • 位比较时,内存二进制完全一致,所以输出num1 == num2 (bitwise),这是内存层面的一致,和浮点数的语义规则无关。

最后补个小知识点

如果你在C里想判断一个值是不是NaN,不能用x == NaN(因为永远返回假),得用标准库的isnan()函数(需要包含<math.h>),这个函数会直接检查浮点数的二进制格式,判断是不是NaN。

核心逻辑就是IEEE 754的标准规定,C只是遵循了这个标准而已~

火山引擎 最新活动