最近看到 0.5+0.1==0.6 输出true 0.1+0.2== 0.3 输出false 很是不解然后就看了下 浮点数储存的问题.
很多博客写的都不怎么好.媒体文章也是晦涩难懂. 有些写的不错但细节很差 我就打算自己写一个, 不废话了 写正文.
/* ============================================================ */
电脑所有数据是二进制储存的大家都知道
1位就是电脑一个最小单位空间 只能储存0或1的值
浮点数就是带小数点的值 1.1啊之类的; 在电脑储存的类型一般有:
float 32位 学名单精度浮点数; double 64位类型 学名双精度浮点数; long double 80~128位(各语言储存位数可能不一样反正都是大于64的 %8==0) ...
意味着他们这些类型用32 64 80~128个最小空间表示一个浮点数; 一般人看到这就觉得这么多位表示一个值为啥表示不清楚?肯定稳了啊..这个坑就略过了就像当初的我.....
这些类型储存分为3段存储的.
第一段 占用空间1位 保存浮点数的正负值 学名数符
第二段 占用空间8 or 11 or >=15位 学名阶码
第三段 占用空间23 or 52 or >=64位 学名尾数
浮点数类型都是由下面这个模式储存的:
| 数符 | 阶码 | 尾数 |
以float为例储存的编码可能为: 0 10000001 11111111111111111111111
加空格方便好看而已没有其他意思. 至于值是多少 我随便写的我也懒得算.=. =
下面介绍各段代表的意思
数符: 值为0 代表 float类型的数为正数 (为了书写运算方便之后都是以float为例)
值为1代表float类型的数为 负数
阶码: 8位 值为N最小为0 最大为 255 表示后面尾数中小数点的位置;
实际是用2^(N-255/2)对尾数的二进制进行乘法
在二进制中 *2代表 所有被乘的数左移一位末尾添加0 例二进制:111 *2=1110 除2: 111/2 =0111 (原1的位置变成0)
那么2^N 中N就代表了这串二进制左移次数N或理解为右侧插入N个0, -N代表右移N次或理解为左侧插入N个0;
255/2是什么鬼 很简单. 左移和右移均衡下的结果. N范围0~255 不能什么都不减 那么就不能右移了. 至于左移右移干啥的下面会讲.
尾数: 某种算法保存浮点数值 的二进制数; 储存时它没有储存最前面位数的1;
光用文字讲太难 举个例子吧: 二进制float: 0 10000001 110100 00000000 00000000
数符 0 正数
阶码: 10000001 为值129,带入 2^(N-255/2) = 2^2=2^a
尾数 1101000 00000000 00000000 实际是 11101000 00000000 00000000 (左侧加个1)
乘法2^2运算后为 11 10100000 00000000 00000000 (右侧插入2个0)
然后找尾数开头位的原地址的 把这串二进制分开 111 0100000 00000000 00000000
111为整数位数; 1*2^2+ 1*2^1 + 1*2^0 = 7
01000000000000000000000 为小数位数 0* 2^(-1) + 1*2^(-2) + ... = 0.25
合并在一起得到 + 7.25
另一种理解方法: 尾数实际是 1 1101000 00000000 00000000
最开始第一位的1表示的实际值为 1*2^(0 + a) 之后每位的2阶乘递减 第五位1表示的实际值为 1*2^(-4+a)
1*2^(0 + 2) + 1*2^(-1+2) + 1*2^(-2+2) + 1*2^(-4+2) = 4+2+1+0.25=7.25
例2 二进制float: 1 01111110 1111000 00000000 00000000
负数 2^(-1) 尾数实际为:1 1111000 00000000 00000000
左侧插一个0 左侧插一个0得到 0 1111100 00000000 00000000 0
0*2^0 =0
1*2^(-1)+ 1*2^(-2) + 1*2^(-3) + 1*2^(-4) + 1*2^(-5)=0.5+0.25+0.125+0.0625+0.03125
合并在一起得到 - 0.96875
现在再想想. 0.1用浮点数表达...... 貌似尾数有很多个1而且表达还不精确. 0.2也是 0.3也是... 这两个尾数通过运算加一起.....
两个计算损失的浮点数加在一起 不等于一个计算损失的浮点数这很正常.
0.5+0.1==0.6 输出true 0.5是精确值 0.1不是精确值 0.6不是精确值 但 0.5就是 1*2^(-1)啊. 0.5+0.1等于拼接了0.6的损失浮点数
现在想想CPU一定很讨厌不能被2^(-N)整除得浮点数..
如有错误请提出渣渣写文章轻喷...