在前面的篇博文中,我们讲解整数和浮点数在内存中的存储结构
参见
深入理解计算机系统-之-数值存储(四)–整数在内存中的存储方式深入理解计算机系统-之-数值存储(五)–浮点数在内存中的存储方式
代码上传至
https://github.com/gatieme/AderXCoding/tree/master/language/c/storage
好了知道了浮点数的存储方式,那么我们的问题来了,如果我们定义了一个浮点数,那么如果以整数的格式去读取它,会发生什么奇妙的现象
我们对上一篇文章中修改main函数为如下形式
我们定义了变量float f = 9.0f
, 然后用一个整形指针去读它,由于32位机器上int和float类型的大小是一致的,不会存储访问越界(即使是64位机器,int也不一定为不是32位,因为这个不仅跟机器字长相关,还跟编译器的处理方式相关)
#include <stdio.h>
#include <stdlib.h>
int print_bit(void *addr, int size)
{
unsigned char *ptr = (unsigned char *)addr;
int print_bytes = 0;
if(ptr == NULL)
{
return -1;
}
for(print_bytes = 0;
print_bytes < size;
print_bytes++, ptr++)
{
#ifdef DEBUG
printf("byte %d, data = %02x -=>", print_bytes, *ptr);
#endif
for(int print_bits = 7;
print_bits >= 0;
print_bits--)
{
printf("%d", ((*ptr >> print_bits) & 1));
}
#ifdef DEBUG
printf("\n");
#endif
}
printf("\n");
return print_bytes;
}
int print_byte(void *addr, int size)
{
unsigned char *paddr = (unsigned char *)addr;
int print_bytes = 0;
if(paddr == NULL)
{
return -1;
}
while(print_bytes < size)
{
printf("%02x", *paddr);
paddr++;
print_bytes++;
}
printf("\n");
return print_bytes;
}
int main(void)
{
printf("%d == %d\n", sizeof(float), sizeof(int));
float f = 9.0f; //对于一块内存,按浮点型初始化
int * pInt = (int *)(&f);
print_byte((void *)&f, sizeof(f));
print_bit((void *)&f, sizeof(f));
printf("the float : %f, %f\n", f, *pInt); //以浮点视角看
printf("the int : %d, %d, %d\n", f, (int)f, *pInt); //以整型视角看
*pInt = 9;
print_byte((void *)&f, sizeof(f));
print_bit((void *)&f, sizeof(f));
printf("the float : %f, %f\n", f, *pInt); //以浮点视角看
printf("the int : %d, %d, %d\n", f, (int)f, *pInt); //以整型视角看
return EXIT_SUCCESS;
}
float 9.0表示出来 = 1001×20=1.001×23
阶码=3 +127 =130 =10000010B
符号位 | 阶码 | 尾数 |
---|---|---|
0 | 10000010 | 00100000000000000000000 |
存储起来就是0x41100000H
这个不需要多说什么,存储格式与读取格式一致
首先,将0x00000009拆分,得到第一位符号位s=0,后面8位的指数E=00000000,最后23位的有效数字M=000 0000 0000 0000 0000 1001。
由于指数E全为0,所以符合上一节的第二种情况。因此,浮点数V就写成:
V=(-1)^0×0.00000000000000000001001×2^(-126)=1.001×2^(-146)
显然,V是一个很小的接近于0的正数,所以用十进制小数表示就是0.000000。
这种方式下,出现了一个奇妙的问题,我们的浮点数9.0f用%d输出后,变成了0.0。
这是为什么呢。因为在C语言中浮点数默认为双精度(double),除非指定F,但是printf打印时对于单精度浮点数,仍然要转换成双精度浮点数然后打印,因此
printf("%d", f) <-==->printf("%d", 9.0f);
会进行如下处理
单精度浮点数:9.0F = 0 | 10000010| 00100000000000000000000
- 求出它的单精度内存表示,
- 转换为双精度,有效位不足补0,
- 输出低32位所表示的十进制数
总的来说,在打印一个浮点数时,一.浮点数转换成双精度 二. 打印低32位.
因此打印的结果始终为0
将浮点数(单双精度)转换为整数时,将舍弃浮点数的小数部分, 只保留整数部分。将整型值赋给浮点型变量,数值不变,只将形式改为浮点形式, 即小数点后带若干个0。
注意:赋值时的类型转换实际上是强制的。
因此显示的结果是9
这个也比较好理解,由于我们用了一个int行的指针去读取
那么编译器处理时会认为这个是一个int型的变量
回到前面float 9.0表示出来是
符号位 | 阶码 | 尾数 |
---|---|---|
0 | 10000110 | 0110010001000000000 |
小端模式的存储结构为00001041,编译器出来以后会·按字节进行整合,
存储的int值其实是0x41100000H = 1091567616
接着我们用整形指针向f中写入了数据 *pInt = 9;
虽然我们看起来好像没什么变化,但是其实写入的数据已经发生了很大变化,因为前面的两篇博文我们已经看到,整数和浮点数的存储格式是有很大区别的。
这个操作其实是将一个单精度浮点数标识的9.0f的存储格式,强制变成了整数9的存储格式。
因此进行浮点数读全为0(阶码为0)
而我们用pInt去按照整数读取的时候,由于存储格式本来就是整数的存储格式,因此读取出来是9
跟上一个例子相反,我们定一个int型变量,然后用float型的指针进行读取
int main(void)
{
printf("%d == %d\n", sizeof(float), sizeof(int));
int num = 9; /* num是整型变量,设为9 */
float* pFloat = (float *)# /* pFloat表示num的内存地址,但是设为浮点数 */
print_byte((void *)&num, sizeof(num));
print_bit((void *)&num, sizeof(num));
printf("the float : %f, %f\n", num, *pFloat); //以浮点视角看
printf("the int : %d, %d\n", (int)num, *pFloat); //以整型视角看
*pFloat = 9.0f; /* 将num的值改为浮点数 */
print_byte((void *)&num, sizeof(num));
print_bit((void *)&num, sizeof(num));
printf("the float : %f, %f\n", num, *pFloat); //以浮点视角看
printf("the int : %d, %d\n", (int)num, *pFloat); //以整型视角看
return EXIT_SUCCESS;
}
同样我们这个代码,用上面的分析就很简单了。也不过是将一个浮点数的存储格式,强制变成了整数的存储格式而已