首先讲讲数在计算机上的表示
数的机器表示
众所周知 机器很笨 它是不认识什么 有符号数 和 无符号数的 它只认识 二进制串 那么在计算机上 是如何表示 有符号数和无符号数的呢?
计算机不区分有符号数和无符号数
首先在计算机眼中 它确确实实只认识 二进制串 二进制串所表示的数 是有符号数还是无符号数 取决于 用户(编码者)怎么看 当你认为 0xffff
为正数时 它就是 65535
当你认为 0xffff
为负数时 就为 -1
其次 在计算机中 它会将送进来的数 统一用补码计算 使用补码计算就不用区分 是有符号数还是无符号数 因为补码的加减法的规则相同
比如 一个二进制为: 1000 0001
当你对其 +1
后 则为 1000 0010
- 若你将
1000 0001
当做无符号数 则为129 + 1 = 130
此时1000 0010
为无符号数的130
- 若视为 有符号数 则为
-127 + 1 = -126
此时1000 0010
为有符号数的-126
也就是说 补码的存在 使得CPU 计算有符号数和无符号数的 加法过程统一 此外为了方便用户 CPU计算完成后 还会设置 eflags 状态寄存器 根据状态位即可同时知道 有符号数和无符号数的运算结果
至于 最高有效位 是 0 还是 1 来表示的正负数 是程序员自己认为的 机器不认识 也不懂 它只知道执行指令
浮点数的表示
根据 IEEE 754 标准
二进制浮点数V 可以表示为 V = (-1)^S * M * 2^E
计算机中浮点数二进制存储结构可以表示为 [sign| exp | frac ]
其中 s 为符号位 和整数的二进制表示不同的是 它只表示符号位 不带任何权重
- 单精度浮点数: exp宽为 8位 frac宽为 23位
- 双精度浮点数: exp宽为 11位 frac宽为52位
规格化浮点数
exp != 000...0 && exp != 111...1
则为规格化浮点数
此时 E + Bias = Exp
- Bias取值
- Bias = 2^e-1 e = exp位数
- 单精度数: 127
- 双精度数: 1023
之所以 E 要 Exp 减去 Bias 的原因是 E是一个 unsigned int
而科学计数法中的 E 是有正有负的 因此 E要减去一个中间数
frac域的第一位隐含为 1 在 32位浮点数中 frac的宽为 23位 省略了第一位以后 则多了一位 可以保存24位
规格化浮点数实例
Float F = 15213.0
- 15213D = 11101101101101B = 1.1101101101101B X 2^13
- M = 1.1101101101101B
- frac= 1101101101101B(规格化浮点数 舍弃第一位)
- E = 13
- Bias = 127(因为是32位浮点数)
- Exp = 13 + 127 = 140 = 10001100B
最终在机器中表示为 [0|1000 1100|1101 1011 0110 1]
非规格化浮点数
exp = 000...0
- E = -Bias + 1 Bias = 2^e-1 e = exp位数
- M = 0.xxxxx
- 当 exp = 000…0, frac = 000…0
- 表示 0
- 有 +0 与 -0
- 当 exp = 000…0, frac != 000…0
- 表示 非常接近于 0
- 会逐步丧失精度
- 当 exp = 111…1, frac = 000…0
- 表示无穷
- 1.0/0.0 = +无穷 -1.0/0.0 = -无穷
- 当 exp = 111…1, frac != 000…0
- NaN
举个例子 Int to Float
1 | int num = 8; |
*pfloat
的值为多少?
1 | 首先 num 为 0x00000008 |
再举个例子 Float to Int
1 | int num = 8; |
1 | num 的值为多少? |
1 | 首先 8.0 是一个正数 符号位 为 0 |