第八章 关于float和double的探讨

1.主要区别

  • 01.在内存中占有的字节数不同
    单精度浮点数在机内存占4个字节
    双精度浮点数在机内存占8个字节
  • 02.有效数字位数不同
    单精度浮点数有效数字8位
    双精度浮点数有效数字16位
  • 03.数值取值范围
    单精度浮点数的表示范围:-3.40E+38~3.40E+38
    双精度浮点数的表示范围:-1.79E+308~-1.79E+308
  • 04.在程序中处理速度不同
    一般来说,CPU处理单精度浮点数的速度比处理双精度浮点数快

注意:float是8位有效数字,第7位数字将会四舍五入

2.精度丢失问题

2.1看一个demo:

package com.hqa.design.test;

public class Test {
	public static void main(String[] args) {
		System.out.println(3 * 0.1);
		System.out.println(3 * 0.1 == 0.3);
		// float是8位有效数字,第7位数字将会四舍五入
		float a = 1.32344435f;
		System.out.println(a);
	}
}

输出:

0.30000000000000004
false
1.3234444

因此,商业运算我们通常使用BigDecimal这个类,而不去使用浮点数,此处有坑!!

2.2 浮点数精度丢失原理

2.2.1 例子
package com.hqa.design.test;

public class Test {
	public static void main(String[] args) {
		double a =1;
		double b =0.99;
		System.out.println(a - b);//输出 0.010000000000000009 发生精度丢失
		
		double a1 = 0.5;
        double b1 = 0.25;
        System.out.println(a1 - b1);//0.25 未发生精度丢失
	}
}

这个原因其实很简单,因为计算机计算数值最终都会转换成二进制数进行计算,但是比方说0.99这样的十进制数是无法使用二进制精确表示的,所以必然会有精度问题,而.5的浮点数转换二进制可以精确表示,所以其不会发生精度丢失,这个读者可以研究一下二进制的小数换算。

所有的整数类型转换成二进制,看如下代码:

//表示11的二进制数计算
11 / 2 = 5 余 1   -->    1
5  / 2 = 2 余 1   -->  	 1
2  / 2 = 1 余 0   -->	 0	
1  / 2 = 0 余 1	  -->    1

所有的小数转换成二进制:看如下代码

//0.1转换为二进制
0.1 * 2 = 0.2  -->0
0.2 * 2 = 0.4  -->0
0.4 * 2 = 0.8  -->0
0.8 * 6 = 1.6  -->1//然后去掉1
0.6 * 2 = 1.2  -->1
0.2 * 2 = 0.4  -->0

可以看到,大部分小数转换2进制都是无休无止的,因此精度丢失必然存在,总之!多用BigDecimal少用浮点数,以免产生不可预料的bug

3.浮点数存储原理

有兴趣的读者,可以参考这篇博文

3.1扩展-为什么0.1无法被二进制小数精确表示?

这个问题首先要从数位表示法说起。今天我们看到的123这样的十进制数,是自然而然的理解其意义,但是有没有深究其内在的数学原理呢?
所谓十进制是0-9十个基本符号为基础的一种数字表示法,数位表示法是将一串基本符号从左到右连续排列的一种方法。为什么12时表示一十二,而不是二十一,或者是一加二的意思呢?因为数字所处的位置是有特别意义的,最右边第一个数字符号,代表基本的数0-9,而第二位的意义并不是0-9,而是0*10-9*10。推而广之,百位是x*100,(x是符号),用简练的数学公式就是x*10^k , 个位k是0,十位是1,百位k是2,从右到左一直数下去。123的意思就是1*10^2+2*10^1+3*10^0

位置,进制,符号这三者的关系就是“123”这种数字表示法内在的数学原理。

那么,0.1 是什么意思?是1*10^-1,向右数数的结果。小数点是为了区分个位的位置在哪里。

一个数要用“数位表示法”表示出来,必然需要能够化为x*10^k的形式,而并不是任意数都能够做到。从数位法小数的定义看可以得知,一个数要能够被表示出来,需要能除尽10,才有若干个x*10^k 的数位组合表示它,否则就是无数个符号才能表示。如1/3这个数除以10等于1/3*1/10 = 0.0333333….循环小数。

究竟哪些数可以用十进制表示哪些不可以?如分母是10的因子和因子的合数,如1,2,5,10,20,50等(整数分母为1,而任意大于1的数的因子都有1和自身,因此整数可以用任意数制精确表示)。

回答题目,为什么0.1无法被二进制小数表示,0.1即1/10这个数要转换成二进制即x*2^k的组合数,必须要除尽2.要注意,2进制只有0,1两个符号,另一个需要注意,二进制被除数右移一位等于2,而非10进制的10。

> 1/10*1/2 = 1/20 1*2*2*2*2 = 32 右移4位
> -20 = 12        商1 12*2 = 24 右移1位
> -20 = 4       商1 4*2*2*2 =32 右移3位
> -20 = 12   商1  可见数字重复了,循环小数无疑 即 0.00011001。

那么2进制能够表示哪些十进制小数,5/10,因为能约成1/2,分母是2的因子。
总结一点,就是位置表示法有其自身的缺陷,并不能在有限的数位,表示众多有理数,这个时候,需要借助分数来帮忙,来避免位置表示法以固定数作分母这个缺点。
如果需要一个可以避免循环小数的数制,不妨试用210进制,因为因子比较多,2*3*5*7 =210
第三章 转载自 http://www.cnblogs.com/Nobel/archive/2013/04/08/3009162.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值