目录
- 1. 整数在内存中的存储
- 1.1 原码、反码、补码
- 1.1 大小端存储
- 1.2.1 字节序分类
- 1.2.2 判断字节序
- 2. 浮点数在内存中的存储
- 2.1 浮点数的存储形式
- 2.2 浮点数的 “ 存 ”
- 2.2.1 S
- 2.2.2 E
- 2.2.3 F
- 2.3 浮点数的 “ 取 ”
- 2.3.1 S
- 2.3.2 E、F
- 3. 浮点数存储例题
1. 整数在内存中的存储
1.1 原码、反码、补码
我们都知道,数据在二进制中都是以 二进制 的形式存储在计算机中,而二进制只有 0 和 1两个数字。
有符号整数的二进制序列中,最高位 称 符号位,表示正负;其他位 称 数值位,表示数值。
无符号整数的二进制序列中,没有符号位,即每一位 均为 数值位。
有多少位具体看 该整数的类型大小,比如 int 类型的
整数 的二进制表示 有三种,分别为 原码、反码和 补码(数据存放在内存中的就是 补码 !)。
整数的三种码都是一样的,负数的则有所不同:
- 原码 是整数数值直接翻译成 二进制 所得。
- 反码 是将 原码 的符号位不变,其他数值位按位取反所得。
- 补码 是 反码 + 1 所得。
- 若从
举个例子 - 整数十进制:
-1
- 原码:1 0 0 0 0 0 0 1
- 反码:1 1 1 1 1 1 1 0
- 补码:1 1 1 1 1 1 1 1
1.1 大小端存储
先来看个例子
// 我们在编译器中创造一个变量, 看看它在内存中的存储情况
int a = 0x11223344; // 用十六进制数值对 变量a 进行初始化赋值
开启调试 打开内存监视窗口 看一下
一开始的时候我也有点懵逼,这里不应该是显示为 11 22 33 44
这样吗?
后来我了解到,当 数据大小 >= 2个字节 ,在内存中存储的时候,就会有 字节序 的问题。
而 存储顺序 就分为 大端存储 和 小端存储。
1.2.1 字节序分类
- 大端存储 :是指数据的 低位字节内容 保存在内存的 高地址 处,而数据的 高位字节内容,保存
在内存的 低地址 处。 - 小端存储 :是指数据的 低位字节内容 保存在内存的 低地址 处,而数据的 高位字节内容,保存
在内存的 高地址 处。
简记:
(内容)(顺序)(内容)(顺序)
高 低 低 高 是 大端
高 高 低 低 是 小端
那么,我们该如何知道当前机器的字节序呢?
1.2.2 判断字节序
- 第一种
int check()
{
int i = 1;
return (*(char*)&i);
}
int main()
{
int ret = check();
if(ret == 1)
{
printf("小端存储\n");
}
else
{
printf("大端存储\n");
}
return 0;
}
- 第二种
int check()
{
union // 在联合体中 a 和 b 共用同一块空间
{
int a;
char b;
}un;
un.i = 1;
return un.b;
}
int main()
{
int ret = check();
if(ret == 1)
{
printf("小端存储\n");
}
else
{
printf("大端存储\n");
}
return 0;
}
2. 浮点数在内存中的存储
2.1 浮点数的存储形式
上图获取来自百度百科: IEEE 754:浮点数表示
根据上面的资料,那么任意浮点数 V(Value) 可以表示为
- (-1) ^ S 表示表示符号位 正负
- F 表示 大于等于1小于2 的 有效数字
- 2 ^ E 表示 指数 位(E 是 unsigned int 无符号整数类型)
举例:
十进制数 9.0 转换称二进制为 1001.0,用科学计数法来表示就是 1.001 x 2 ^ 3
那么, S = 0, F = 1.001,E = 3
单精度的浮点数 和 双精度的浮点数 在内存分配的空间是不同的
- float 类型的浮点数内存分配
- double 类型的浮点数内存分配
2.2 浮点数的 “ 存 ”
2.2.1 S
首个比特位存 S 表示浮点数的 正负(0 或 1)
2.2.2 E
前面我们了解到,E 是一个无符号整数,在 float类型浮点数中有占 8个比特位,取值范围 0 ~ 255,
在 double类型浮点数中占 11个比特位,取值范围 0 ~ 2047,也就是说 E 是一个非负整数。
但在科学计数法,是允许存在 负数的 E 存在的。
所以,IEEE 754 制定了规定:
在 E 存入内存时,其真实值必须加上其取值范围的中间数,float类型的为 127,double类型的则为
1023. 比如,2 ^ 8 的 E 是 8,在存入内存时要 + 127 = 135(假设是单精度浮点数类型),即为二进制 10000111
资料来自百度百科IEEE 754:指数偏差
偏移值就是 内存值(E存入内存值) 和 真实值(E原值) 的差
2.2.3 F
F 是 大于等于1小于2 的,对于任何浮点数都几乎可以表示 1.xxxxxxxxx
在 “ 存 ” 的时候,F 只存小数部分的 xxxxxxxxx ;在 “ 取 ” 的时候,再把整数部分的 1 加上去
有啥作用?
(假设是单精度浮点数类型)F在存的时候占23位bit,如果把整数的 1 也存进去的话,小数部分能存22位。 但是如果在 “ 存 ”
的时候,F 只存小数部分的 xxxxxxxxx ;在 “ 取 ” 的时候,再把整数部分的 1 加上去,
那就可以节省1位有效数字,小数部分就可以存23位多一位。
2.3 浮点数的 “ 取 ”
2.3.1 S
S (0 或 1)就正常取,是啥取啥就完了
2.3.2 E、F
- E 不全为 0 或 不全为 1
这种情况下只要把 E 减去原来加上的中间值127(float)或1023(double)得到真实值的 E
F 把整数部分的 1 加上小数部分即可
- E 全为 0
这种情况表示 浮点数是一个接近0的很小的数 或者就是 0
E 减去原来加上的中间值127(float)或1023(double)得到真实值的 E
F 不再加上整数部分的 1 ,而是还原为 0.xxxxxxxxx 的小数
- E 全为 1
如果 F 全为 0,则表示这是一个 正无穷大 或 负无穷小 的 浮点数(取决于符号位 S)
E 减去原来加上的中间值127(float)或1023(double)得到真实值的 E
3. 浮点数存储例题
#include <stdio.h>
int main()
{
int n = 9;
float *pFloat = (float *)&n;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
*pFloat = 9.0;
printf("n的值为:%d\n",n);
printf("*pFloat的值为:%f\n",*pFloat);
return 0;
}
输出结果是什么?
为何有这样的变化?
我们可以看到 指数 E 部分全为0,说明转化后的小数是一个接近0的很小的数,
0.000000000...后面不全为0,但不为0的数也是在非常后面
而我们使用 printf 函数打印浮点数的时候,再没有限定小数位数的情况下, 默认输出小数点后6位
所以我们看到输出结果的第二行就是
*pFloat的值为:0.000000 // 后面的就不输出了
继续往下,为何第二次打印 n 的值,会变得这么大?
而 01000001000100000000000000000000
转化为十进制就为 1091567616
Stay hungry. Stay Foolish. 饥渴求知,虚怀若愚。
感谢各位读者支持,虚心请教,如有错漏或可改进点,请任意指出,感激不尽!
一起进步!