用%d打印float变量为何输出负数?求补码相关解释
问题:float用%d打印输出异常的原因解析
我在自定义硬件上写C/C++代码时,因为硬件不支持UART FTDI,没法用%f打印float类型。于是写了测试代码,尝试用%d打印float值,同时通过指针查看它的内存数据。
测试代码
#include <stdio.h> int main() { float a = 6.7; printf("float a = %f\n", a); unsigned int a_int = *(int*)&a; printf("intbufa = %d\n", a_int); printf("hex a = 0x%x\n", a); printf("int a = %d\n", a); int comp_1s = ~a_int; printf("comp_1s = %d\n", comp_1s); int comp_2s = ~a_int + 1; printf("comp_2s = %d\n", comp_2s); }
程序输出
float a = 6.700000 intbufa = 1087792742 hex a = 0xc0000000 int a = -1073741824 comp_1s = -1087792743 comp_2s = -1087792742
我搞不懂int a = %d这一行的输出,记得和补码有关,求解释。
原因说明
核心问题是printf的格式符和传入参数类型不匹配,这属于C语言里的未定义行为,但结合32位硬件环境可以拆解出具体逻辑:
先明确6.7的float内存布局:
6.7用IEEE 754单精度浮点表示的二进制是0x40D66666,转换成无符号整数就是1087792742,这和你用*(int*)&a取出的a_int值一致,说明内存里的二进制数据是正确的。为什么
printf("%d", a)会输出-1073741824?- 因为printf是可变参数函数,传入的float类型参数会被自动提升为double(C语言的可变参数规则:float会提升为double,char/short会提升为int)。
- 6.7的double类型表示是64位的
0x401A666666666666。 - 而
%d格式符告诉printf要读取一个32位的有符号整数,它会从参数栈中截取32位数据(具体取哪32位取决于硬件的栈布局和端序)。你这里输出的0xc0000000就是被截取的那32位。 - 把
0xc0000000当作有符号int的补码解析:最高位是1,表示负数;剩下的31位是0x40000000,转换成十进制是1073741824,所以补码对应的负数就是-1073741824——这就是你看到的输出。
正确的做法:
如果想打印float对应的内存二进制数据,应该用你之前的方法:unsigned int a_int = *(int*)&a;,把float的地址强制转成int指针再取值,这样拿到的才是float本身的32位二进制数据,再用%d或%x打印才符合预期。
内容的提问来源于stack exchange,提问作者Caliber X
相关产品推荐
相关产品推荐

