浮点数精度问题以及原理解释
⾸先计算机是以⼆进制来存储数值的,需要和⼗进制进⾏转换
⼀、⼆进制数转换成⼗进制数
由⼆进制数转换成⼗进制数的基本做法是,把⼆进制数⾸先写成加权系数展开式,然后按⼗进制加法规则求和。这种做法称为"按权相加"法。
例1105 把⼆进制数110.11转换成⼗进制数。
⼆、⼗进制数转换为⼆进制数
⼗进制数转换为⼆进制数时,由于整数和⼩数的转换⽅法不同,所以先将⼗进制数的整数部分和⼩数部分分别转换后,再加以合并。
1. ⼗进制整数转换为⼆进制整数
⼗进制整数转换为⼆进制整数采⽤"除2取余,逆序排列"法。具体做法是:⽤2去除⼗进制整数,可以得到⼀个商和余数;再⽤2去除商,⼜会得到⼀个商和余数,如此进⾏,直到商为零时为⽌,然后把先得到的余数作为⼆进制数的低位有效位,后得到的余数作为⼆进制数的⾼位有效位,依次排列起来。
例1107 把 (173)10 转换为⼆进制数。
2.⼗进制⼩数转换为⼆进制⼩数
⼗进制⼩数转换成⼆进制⼩数采⽤"乘2取整,顺序排列"法。具体做法是:⽤2乘⼗进制⼩数,可以得到积,将积的整数部分取出,再⽤2乘余下的⼩数 部分,⼜得到⼀个积,再将积的整数部分取出,如此进⾏,直到积中的⼩数部分为零,或者达到所要求的精度为⽌。
然后把取出的整数部分按顺序排列起来,先取的整数作为⼆进制⼩数的⾼位有效位,后取的整数作为
低位有效位。
【例1108】把(0.8125)转换为⼆进制⼩数。
例1109 (173.8125)10=()2
解:由[例1107]得(173)10=(10101101)2
由[例1108]得(0.8125)10=(0.1101)2
把整数部分和⼩数部分合并得: (173.8125)10=(10101101.1101)2
⼗进制⼩数转换成⼆进制⼩数采⽤"乘2取整,顺序排列"法。具体做法是:⽤2乘⼗进制⼩数,可以得到积,将积的整数部分取出,再⽤2乘余下的⼩数部分,⼜ 得到⼀个积,再将积的整数部分取出,如此进⾏,直到积中的整数部分为零,或者整数部分为1,此时0或1为⼆进制的最后⼀位。或者达到所要求的精度为⽌。
  然后把取出的整数部分按顺序排列起来,先取的整数作为⼆进制⼩数的⾼位有效位,后取的整数作为低位有效位。
⾸先要了解,从根本上讲,计算机只识别⼆进制数,⽆论我们使⽤何种编程语⾔,在何种编译环境下⼯作,都需要将程序编译成⼆进制机器码才能让计算机执⾏
那么这样会导致什么问题呢?会导致⼩数的⼗进制转为⼆进制出现误差,这就是所谓的精度缺失。
下⾯转载⼀下⼤神的理解
为什么会出现误差?这就涉及到了计算机原理的问题:
我们知道数字在计算机中是以⼆进制存储的。对于整数来说,只要不超过整数的表⽰范围,⼀定都可以表⽰成⼆进制的形式,⽐如8是100,88是1011000,可是⼩数⼩数部分就没有那么幸运了,根据⼆进制转换成⼗进制的规则(把该⼩数不断乘2,再取所得的整数部份,直⾄没有⼩数或⾜够长度为⽌)。
由于⼆进制中所有的⼩数存储的位数是有限,因此我们得知“任何⼗进制整数都可以精确转换成⼀个⼆进制整数,但任何⼗进制⼩数却不⼀定能精确转换成⼀个⼆进制⼩数,只要转换过程中乘积的⼩数部分满⾜所需精度即可”。⽐如对于0.1来说就不能精确转换为⼀个⼆进制⼩数,在16位⼩数的限制条件下,离它最近的⼆进制⼩数是0.0001100110011,也就是⼗进制的0.0999755859375。所以虽然你写程序的时候写的是0.1开始这个数存储到b这个float变量⾥的时候就变成了0.0001100110011,也就是0.0999755859375,因此你输出它的时候就会出现精度误差了,这种误差是不可避免的。
再来,根据IEEE754(⼆进制浮点数算术标准) 来说:
V = (-1)^s × 2^E × M
  (1)(-1)^s表⽰符号位,当s=0,V为正数;当s=1,V为负数。
(2)2^E表⽰指数位。
(3)M表⽰有效数字,⼤于等于1,⼩于2。
IEEE 754规定,有四种精度的浮点数:单精确度(32位)、双精确度(64位)、延伸单精确度(43⽐特以上,很少使⽤)与延伸双精确度(79⽐特以上,通常以80位实现)。
对于32位的浮点数,最⾼的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。
32位浮点数存储格式
对于64位的浮点数,最⾼的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
64位浮点数存储格式
其实看看上图你就应该明⽩,还有⼀种精度丢失就是长度丢失(不同类型存储结构不同),但那种⽐较简单,就不做讨论。
继续来讲,上图到底是什么意思呢。咱们先慢慢来讲:
正规化(规约形式)
如果浮点数中指数部分的编码值在[图⽚上传失败…(image-c806de-1554705679571)]之间,且在科学表⽰法的表⽰⽅式下,分数(fraction) 部分最⾼有效位(即整数字)是1,那么这个浮点数将被称为规约形式的浮点数。
如上存储格式中所⽰:
⾸先对于第⼀段sign,就是符号位,0代表正,1代表负。。
第⼆段exponent指数位,实际也是有正负的,但是没有单独的符号位,在计算机的世界⾥,进位都是⼆进制的,指数表⽰的也是2的N次幂,8位指数表达的范围是0到255,⽽对应的实际的指数是-127到128。也就是说实际的指数等于指数位表⽰的数值减127。这⾥特殊说明,-127和+128这两个指数数值在IEEE当中是保留的⽤作多种⽤途的,
关于指数位与实际的指数之差叫做移码,移码的值
精度 M(阶/指数数位) 移码 ⼆进制表⽰
单精度 8 127 0111 1111
双精度 11 1023 011 1111 1111
长双精度 15 16383 011 1111 1111 1111
第三段fraction尾数位(也叫有效数字),只代表了⼆进制的⼩数点后的部分,⼩数点前的那位被省略了,当指数位全部为0时省略的是0否则省略的是1。
举个栗⼦(以32位浮点数为例,这样误差⽐较⼤):
⽐如有个浮点数17.35需要保存。
17.35转为⼆进制:17转为10001(这就不⽤说了吧)
0.35呢?(乘2取整法)
0.35 * 2 = 0.7 记为 0
浮点型变量float0.70 * 2 = 1.4 记为 1
0.40 * 2 = 0.8 记为 0
0.80 * 2 = 1.6 记为 1
…这么算有结束吗?当然没有…所以就按照能保存的极限位数保存相对精度。
这样我们17.35就变成了10001.0101100110011001100共预留24位数
然后把这串数字右移⾄⼩数点前只剩1位:
1.00010101100110011001100—》 右移了4位
这样得到了指数位和尾数位:
指数:实际为4,必须加上127(转出的时候,减去127),所以为131。也就是10000011。
尾数:00010101100100100100100 (1就不⽤保存了)。
到此,⼗进制浮点数就变成了⼆进制浮点数存储了。
⾮正规化:0的表⽰(⾮规约形式)(-127)
如果浮点数的指数部分的编码值是0,分数部分⾮零,那么这个浮点数将被称为⾮规约形式的浮点数。分数部分全为0,就是表⽰浮点数0
⽐如说我们要存储0.35这个浮点数,按上述正规化的形式就⽆法存储,因为不知道指数位写多少,于是就有了约定,约定指数位为0就表明⾮正规化。
所以,当见到指数部分为0是,尾数部分就不再是1.bbbbb…⽽是0.bbbbb…了。
⽆穷⼤与NAN
上⾯说了,指数位预留了两个值(-127和128),-127是做⾮规约形式的,那么128呢?
当指数位达到当前浮点数最⼤指数值时:
尾数位全为0就表⽰⽆穷⼤。
尾数位不全为0就表⽰NaN。
舍⼊规则
还是以32位单精度浮点数为例。
32位单精度浮点数存储23位尾数位,那么就以第24位位判断依据
如果 24位为1 24位之后都是0 :如果23位为0,则舍去不管;如果23位为1,则24位向23位进1,使23位还是0。
如果 24位为1 24位之后不全0 :24位向23位进1。
如果 24位为0 舍
** 因为位数、算法、规则之类的约定,得知计算机浮点数并不是严格存储这就是为什么浮点数运算出现问题的原因 **
有事情要做,先写到这。
待说明问题:
关于渐进式下溢出的理论与由来。
关于以下代码出现情况的说明:
System.out.println(0.1 * 1);
System.out.println(0.1 * 2);
System.out.println(0.1 * 3);
System.out.println(0.1 * 4);
System.out.println(0.1 * 5);
System.out.println(0.1 * 6);
System.out.println(0.1 * 7);
System.out.println(0.1 * 8);
System.out.println(0.1 * 9);
System.out.println("=====================");
System.out.println(0.0999999999999999999999999999999999999999999999999); double f1 = 9.7;
double f2 = 0.7;
double f3 = 9;
System.out.println(f1-f2-f3);
System.out.println(f1-f3-f2);
输出结果:
0.1
0.2
0.30000000000000004
0.4
0.5
0.6000000000000001
0.7000000000000001
0.8
0.9
0.1
-6.661338147750939E-16
问题:
为什么有的输出正确,有的不⾏
为什么0.0999–的输出为0.1
为什么f1-f2-f3得到0.0,⽽f1-f3-f2却得到那样的结果
这些问题我现在也并没有思考清楚,等待后续思考。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。