zoukankan      html  css  js  c++  java
  • 浅谈浮点数(一)

    小数与浮点数

    很多人都会认为,小数就是浮点数。但其实非也。
    小数只是一种实数的一种特殊表现形式,所有分数都可以用小数来表示。
    而浮点数,是计算机领域的一个术语,浮点数代表着目前计算机表示小数的一方式。
    

    浮点数的由来

    我们都知道计算机表示特定的数据类型长度是固定的。
    比如在java语言里,小数的表示,float是4字节,double是8字节。
    那么这些固定长度的二进制位是如何表示小数的呢?
    最直观的表示办法就是:固定的整数部分位数和固定的小数部分位数。比如以float为例,我们假设取前8位表示整数部分,后24位表示小数部分。则1.2用该方法表示如下:
    
    	00000001 00000000 00000000 00000000 00000010
    
        以上这种表示小数的方法我们称之为:定点表示法,即小数点的位置是固定的(这里固定在第24位之前)。
    但是这种定点表示法有一个很大的问题,就是表示数的范围很有限。假设我现在要表示:256.1
    那么因为整数部分固定只有8位,将无法表示256,会出现溢出。
    
       于是乎聪明的计算机科学家想到了另一种办法:科学计数法。我们知道10进制下的科学计数法可以将一个数表示成: 1.xxx * 10^n 。
    依葫芦画瓢,那么2进制的科学计数法应该长这样:1.xxx * 2^n 
    那么我们在存储小数的时候,可以用一部分存储指数:n,一部分存储小数:xxx 即可。
    而这种表示的方式下,其实小数点没有固定的位置,既小数点是浮动的。所以我们也就称这种存储方式下的数字为浮点数。
    

    浮点数的存储规范:IEEE 754

      IEEE二进制浮点数算术标准(IEEE 754)是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用。
    这个标准定义了表示浮点数的格式(包括负零-0)与反常值(denormal number)),一些特殊数值(无穷(Inf)与非数值(NaN)),以及这些数值的“浮点数运算符”;
    它也指明了四种数值舍入规则和五种例外状况(包括例外发生的时机与处理方式)。
    说人话就是:一个浮点数可以表示如下:
    
    	value = sign x exponent x fraction 
    	其中value表示浮点数的实际值
    	sign(bit)表示符号位: 0表示整数 1表示负数
    	exponent表示的是转换成科学计数法后的指数偏移值
    	fraction表示小数部分
    
       知道浮点数的具体表示方式之后,接下来就是要确定每一部分所占的长度。
    在IEEE 754标准中,对于32位浮点数的各部分长度约定如下:
        ·1bit的sign + 8bit的exponent + 23bit的fraction·
    而对于64位的浮点数的各部分长度约定如下:
        ·1bit的sign + 11bit的exponent + 52bit的fraction·
    
    我们前面说过exponent并不是科学计数法之后的实际指数,而是代表科学计数法后的指数偏移量。那么怎么个偏移法呢?
    其实在IEEE 754中也对这个做了规定。我们假设k表示exponent所占的总位数,n表示转换成科学计数法之后的实际指数值,那么最终exponent = 2^(k-1) + n 
        
    为什么要这么设计呢?我们知道小数可能是不带整数的,这时候如果转换成科学计数法之后实际指数值就应该是负数。
    对于指数为负数的情况,我们很自然地会想到用exponent部分的第一位表示正负,然后对于负数值采用补码的方式来表示(取反加一)。
    而原来整个value值也有一个sign位表示正负,剩余位在小数为负数的时候也需要使用补码方式来表示。
    我们假设这样一种情况:指数为负数且小数为负数,那么对exponent部分的两次取反加1会导致最终结果不可预知。
    
    	因此,最后IEEE 754采用了:exponent = 2^(k-1) + n 这种方式来存储指数的偏移值。
    

    java中如何查看浮点数的二进制表示

    我们可以使用如下两行代码来查看0.1分别在32位和64位下的二级制形式:
    
         System.out.println(Integer.toString(Float.floatToIntBits(0.1f), 2)); // 111101110011001100110011001101
         System.out.println(Long.toString(Double.doubleToLongBits(0.1), 2)); // 11111110111001100110011001100110011001100110011001100110011010
    
    我们将高位补0,并且按照前面所讲的sign + exponent + fraction的形式将两者拆解如下:
    
    0 01111011    10011001100110011001101
    0 01111111011 1001100110011001100110011001100110011001100110011010
    
    要将一个小数转换成浮点数的形式,首先要求得小数的二进制表示法。0.1的整数部分为0,整数部分的如果用8位表示则为:00000000。
    小数部分的0.1如何转换成2进制呢?这里我们仍然要从10进制小数来进行推导。
    
    我们假设计算机是以10进制的形式来存储数据的。那么对于0.631,小数部分第1位存储的应该直接就是6,也就是0.631 * 10 的整数部分。
    第2位存储的应该就是3,也就是 0.31 * 10 (在第一步去掉整数部分之后再乘以10的整数部分)。同理第3位存储的就是1,0.1 * 10。
    
    于是乎我们可以得到0.1作为二进制在计算机中的存储:
    
    	第一位: 0.1 * 2 = 0.2 的整数部分  0
    	
    	第二位: 0.2 * 2 = 0.4 的整数部分  0
    	第三位: 0.4 * 2 = 0.8 的整数部分  0
    	第四位: 0.8 * 2 = 1.6 的整数部分  1  ---》再去掉整数部分后为0.6
    	第五位: 0.6 * 2 = 1.2 的整数部分  1  ---》再去掉整数部分后为0.2
    	
    	第六为: 0.2 * 2 = 0.4 的整数部分  0
    	第七位: 0.4 * 2 = 0.8 的整数部分  0
    	第八位: 0.8 * 2 = 1.6 的整数部分  1  ---》再去掉整数部分后为0.6
    	第九位: 0.6 * 2 = 1.2 的整数部分  1  ---》再去掉整数部分后为0.2
    	
    	.....
    	
    
    综上,我们得到0.1的二进制存储应该为:0001100110011...(0011循环)。
    于是,0.1的整个二进制表示为: 00000000.0001100110011...(0011循环)
    转换成科学计数法为:1.100110011...(0011循环) * 2^(-4)。
    按照IEEE 754标准,如果是32位的表示法,那么exponent = 2 ^ 7 + (-4) = 01111011
    如果是64位表示法,则exponent = 2 ^ 10 + (-4) = 01111111011
    再按照 sign + exponent + fraction的表示方法拼接起来即得到32位和64位的表示分别如下:
    	0 01111011    100110011001100110011...(0011循环)
    	0 01111111011 100110011001100110011001100110011001100110011...(0011循环)
    
    最后剩下的问题就是:小数的存储位数是固定的,那么如果将循环的部分截断呢?这就涉及到舍入规则。
    舍入的规则如下:即如果左规或右规时丢弃的是0,则舍去不计,反之要将尾数的末尾加1。
    我们同样以0.1为例,32位情况下,小数部分的最终表示如下:10011001100110011001101
    我们知道小数部分最后是0011循环,所以最后一位数字本来应该是0,但是因为紧接着的是1,所以最终截取之后还需要进行加1操作,于是就得到1。
    64位的表示法同样也可以根据这个规则得到。
    

    黎明前最黑暗,成功前最绝望!
  • 相关阅读:
    Project Euler 389 Platonic Dice (概率)
    单纯形(相关题目)
    关于C++中的内存泄露
    莫比乌斯反演与积性函数求和筛法中的一些细节
    清华集训2015-Day 2
    bzoj3456-城市规划
    多项式运算的一些技术
    bzoj2302-Problem c
    bzoj4300-绝世好题
    bzoj4726-Sabota?
  • 原文地址:https://www.cnblogs.com/Kidezyq/p/15091182.html
Copyright © 2011-2022 走看看