浮点数在计算机二进制储存的问题

最近看到 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)整除得浮点数..

 

如有错误请提出渣渣写文章轻喷...

你可能感兴趣的:(内存存储问题)