共计 3645 个字符,预计需要花费 10 分钟才能阅读完成。
Float32 转二进制
C# 中浮点数的二进制格局遵循 IEEE754 规范(IEEE 二进制浮点数算术规范)。
以小数 11.25 为例,float32 的二进制值为:
01000001001101000000000000000000
这个值是怎么来的呢?
IEEE754 是一个对于浮点数的规范,它把浮点数分成 3 个局部:Sign(符号)|Exponent(指数)|Mantissa(尾数)
Sign(符号) 示意浮点数的正负 (大于等于 0 为 0,小于 0 为 1)
Exponent(指数) 示意浮点数的指数(相似迷信计数法的指数局部)
Mantissa(尾数) 示意有效数字(相似迷信计数法的有效数字)
以下内容简称这 3 局部别离为 S、E、M
Float32 占 32 位,8 位一字节共 4 字节:
S(1 位)|E(8 位 偏移 127)|M(23 位) 即:0 | 1000 0010 | 01101000000000000000000
S、E、M 这 3 局部是怎么确定的呢?
比方 11.25 示意成十进制的迷信计数法: 1.125×101
符号是 +,指数是 1,有效数字是 1.125
IEEE754 是先把小数转成二进制,用二进制的迷信计数法示意该小数。
还是以 11.25 为例:
S 为 0 (这个数字大于等于 0 符号 S =0,如果小于 0,符号 S =1)
把浮点数的整数局部和小数局部别离转成二进制,再拼到一起。
整数局部转二进制:
转换方法: 整数除以 2 取余 倒序排列 (除到整数局部为 0)
整数局部 I 为 11,转成二进制为:1011
$$
2\sqrt{11}=5···1 \qquad\enspace
\\=2···1
\\=1···0
\\=0···1
$$
小数局部转二进制:
转换方法: 小数乘以 2 取整 正序排列 (乘到小数局部为 0,即余数为 0)
小数局部 F 为 0.25,转成二进制为:01
$$
0.25\times2=0···0.5 \qquad\enspace\enspace
\\=1···0
$$
整数和小数拼到一起:
IF = 1011.01
以二进制迷信计数法表白:
IF = 1.01101×23
指数局部 E:
E = 3, IEEE754 规定这个值要加 127
E = 130 转成二进制为:1000 0010
整数除以 2 取余 倒序排列(除到整数局部为 0)
$$
2\sqrt{130}=65···0 \qquad\enspace\enspace
\\=32···1
\\=16···0
\\=\enspace 8···0
\\=\enspace 4···0
\\=\enspace 2···0
\\=\enspace 1···0
\\=\enspace 0···1
$$
尾数局部 M:
M = 101101
因为二进制的迷信计数法首位肯定为 1, 1 能够省略不写,尾数局部就变成了 01101
M = 01101
S、E、M 三局部拼到一起:
0 | 1000 0010 | 01101 (前面补 0 够 23 位)
0 | 1000 0010 | 0110 1000 0000 0000 0000 000
11.25 转成二进制最终后果就是 0 1000 0010 0110 1000 0000 0000 0000 000
用《在线浮点数转换工具》验证一下,后果正确。
二进制转 Float32
二进制 11000000110110000000000000000000 转 Float32 怎么转呢?
还是先把二进制分为 S、E、M 三局部,别离转换后计算最终后果。
1 | 1000 0001 | 1011 0···
S = 1 符号为 - 是正数
E = 1000 0001
M = 1011 0···
指数 E 转整数:
E = 1000 0001 转整数
转换方法为: 从低位到高位 按位转 10 进制相加
1×27 + 0x26 + 0x25 + 0x24 + 0x23 + 0x22 + 0x21 + 1×20 =
128 + 0 + 0 + 0 + 0 + 0 + 0 + 1 = 129
减去 127(转二进制之前加了 127 还原时须要减回来)
E = 129 – 127 = 2
尾数 M 转小数:
M = 1011
首位补 1(为了节俭空间 转换时去掉了首位的 1 还原时须要补回来)
M = 11011
有效数字 迷信计数法
M = 1.1011 x 22
M = 110.11
整数局部 I = 110 小数局部 F = .11
整数局部 I 转 10 进制:
I = 110 转成 10 进制为:6
1×22 + 1×21 + 0x20 =
4 + 2 + 0 = 6
I = 6
小数局部 F 转 10 进制:
1x(1/21) + 1x(1/22) =
1×0.5 + 1×0.25 =
0.5 + 0.25 = 0.75
F = 0.75
整数和小数局部拼接到一起:
6 + 0.75 = 6.75
加上符号位:
S = 1 (正数 符号为 -)
float32 = -6.75
二进制 11000000110110000000000000000000 转成十进制最终后果就是 -6.75
用《在线浮点数转换工具》验证一下,后果正确。
Float64 转二进制
IEEE754 规范中规定,Float64 占 64 位共 8 字节 S、E、M 三局部别离为:
S(1 位)|E(11 位 偏移 1023)|M(52 位)
以 9.625 为例:
S = 0 (负数)
整数局部转二进制:
转换方法: 整数除以 2 取余 倒序排列 (除到整数局部为 0)
整数局部 I = 9 转二进制为:1001
$$
2\sqrt{9}=4···1 \qquad
\\=2···0
\\=1···0
\\=0···1
$$
小数局部转二进制:
转换方法: 小数乘以 2 取整 正序排列 (乘到小数局部为 0,即余数为 0)
小数局部 F 为 0.625,转成二进制为:101
$$
0.625\times2=1···0.25 \qquad\enspace\enspace\enspace\enspace
\\=0···0.5
\\=1···0\enspace
$$
整数和小数拼到一起:
IF = 1001.101
以二进制迷信计数法表白:
IF = 1.001101 x 23
指数局部 E:
E = 3, 加上偏移值 1023
E = 1026 转成二进制为:1000 0000 010
转换方法: 整数除以 2 取余 倒序排列(除到整数局部为 0)
$$
2\sqrt{1026}=513···0 \qquad\enspace\enspace\enspace
\\=256···1
\\=128···0
\\=\enspace 64···0
\\=\enspace 32···0
\\=\enspace 16···0
\\=\enspace\enspace 8···0
\\=\enspace\enspace 4···0
\\=\enspace\enspace 2···0
\\=\enspace\enspace 1···0
\\=\enspace\enspace 0···1
$$
尾数局部 M:
M = 1001101
因为二进制的迷信计数法首位肯定为 1, 1 能够省略不写,尾数局部就变成了 001101
M = 001101
S、E、M 三局部拼到一起:
0 | 1000 0000 010 | 001101 (前面补 0 够 52 位)
9.625 转成二进制最终后果就是 0 1000 0000 010 001101 0···
用《在线浮点数转换工具》验证一下,后果正确。
二进制转 Float64
二进制 110000000101100100010···转 Float64 怎么转呢?
还是先把二进制分为 S、E、M 三局部,别离转换后计算最终后果。
1 | 1000 0000 101 | 1001 0001 0···
S = 1 符号为 - 是正数
E = 1000 0000 101
M = 1001 0001 0···
指数 E 转整数:
E = 1000 0000 101 转整数
转换方法为: 从低位到高位 按位转 10 进制相加
1×210 + 0x29 + 0x28 + 0x27 +
0x26 + 0x25 + 0x24 + 0x23
1×22 + 0x21 + 1×20 =
1024 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 4 + 0 + 1 = 1029
减去 1023(转二进制之前加了 023 还原时须要减回来)
E = 1029 – 1023 = 6
尾数 M 转小数:
M = 1001 0001
首位补 1(为了节俭空间 转换时去掉了首位的 1 还原时须要补回来)
M = 1100 1000 1
有效数字 迷信计数法
M = 1.10010001 x 26
M = 1100100.01
整数局部 I = 1100100 小数局部 F = .01
整数局部 I 转 10 进制:
I = 1100100 转成 10 进制为:100
1×26+ 1×25 + 0x24 + 0x23 + 1×22 + 0x21 + 0x20 =
64 + 32 + 0 + 0 + 4 + 0 + 0 = 100
I = 100
小数局部 F 转 10 进制:
0x(1/21) + 1x(1/22) =
0x0.5 + 1×0.25 =
0 + 0.25 = 0.25
F = 0.25
整数和小数局部拼接到一起:
100 + 0.25 = 100.75
加上符号位:
S = 1 (正数 符号为 -)
float64 = -100.25
二进制 110000000101100100010··· 转成十进制最终后果就是 -100.25
用《在线浮点数转换工具》验证一下,后果正确。
总结:
大多数程序语言中,对于浮点数的示意都遵循 IEEE754 规范,都是由 S、E、M 三个局部组成,相似科学技术法的示意规定,为了尽量节约空间做了某些非凡规定。因为转换时,某些数据比方 0.1 小数局部乘 2 当前不能齐全乘尽(0.2, 0.4, 0.8, 0.6 …… 有限循环) 所以小数会呈现失落精度的状况,不能齐全精确的表白这些乘不尽的状况,所以遇到相似精度失落,数据变大了一点点,变小了一点点的问题,晓得是这个原理导致的问题即可,进步精度或四舍五入一下即可解决。