学习java浮点型的一些细节
出自《Java深入解析》的例子
例1、先看一段代码:
public static void main(String[] args) { double d1 = 0.1; double d2 = 0.2; System.out.println("" + d1 + "+" + d2 + "=" + (d1 + d2)); }
初学者可能脱口而出执行的结果是输出 d1+d2=0.3。但是结果却没有这么简单:
0.1+0.2=0.30000000000000004
转念一想,却也没有一点玄妙。
二进制只能表示十进制中诸如0.5,0.25,0.125之类的小数。
十进制中可以表示0.1~0.9的小数,却不能表示1/3这样的分数。以此类推需要表示1/3,可能3进制更能胜任。
例2、BigDecimal是用来表示精度较大的小数的类,用二进制的数据表示十进制数可以说真是强人所难了。
for (int i = 1; i <= 9; i++) { double d = Double.parseDouble("0."+i); System.out.println(d); BigDecimal bd = new BigDecimal(d); System.out.println(bd); }
结果:
0.1 0.1000000000000000055511151231257827021181583404541015625 0.2 0.200000000000000011102230246251565404236316680908203125 0.3 0.299999999999999988897769753748434595763683319091796875 0.4 0.40000000000000002220446049250313080847263336181640625 0.5 0.5 0.6 0.59999999999999997779553950749686919152736663818359375 0.7 0.6999999999999999555910790149937383830547332763671875 0.8 0.8000000000000000444089209850062616169452667236328125 0.9 0.90000000000000002220446049250313080847263336181640625
例3.不如做个测试,看看计算机“偏爱”什么样的数字吧。
1 /** 2 * 测试0.0001~0.9999范围内的小数那些可以被二进制准确表示 3 * */ 4 public class FloatStorage { 5 6 public static void main(String[] args) { 7 int limit = 9999;// 测试的范围0.0001-0.9999 8 int length = String.valueOf(limit).length();// 小数的位数 9 10 System.out.println("在0.0001~0.9999之间可以被准确表示的小数有"); 11 12 for (int i = 1; i <= 9999; i++) { 13 int significance = String.valueOf(i).length();// 小数的非0数位 14 int zero = length - significance;// 小数的0数位 15 StringBuilder sb = new StringBuilder("0.");// 建立字符串 16 for (int j = 1; j <= zero; j++) { 17 sb.append(0); 18 } 19 sb.append(i); 20 BigDecimal bd = new BigDecimal(Double.parseDouble(sb.toString())/* 得到二进制数 */); 21 if (bd.scale() <= length) {// 如果符合我们的要求,输出 22 System.out.println(bd); 23 } 24 } 25 } 26 27 }
运行结果:
在0.0001~0.9999之间可以被准确表示的小数有
0.0625
0.125
0.1875
0.25
0.3125
0.375
0.4375
0.5
0.5625
0.625
0.6875
0.75
0.8125
0.875
0.9375
真是寥寥无几啊!
如果小结一下浮点数的特点,原来浮点数和小数之间还是有一定的差别,
在赋值或者存储中浮点类型的精度有限,
同时在计算机实际处理和运算过程中,浮点数本质上是以二进制形式存在的。
二进制所能表示的两个相邻的浮点值之间存在一定的间隙,浮点值越大,这个间隙也会越大。
如果此时对较大的浮点数进行操作时,浮点数的精度问题就会产生,甚至出现一些“不正常"的现象。
例4
public class BigFloat { public static void main(String[] args) { float f1 = 30000f; float f2 = f1 + 1; System.out.println("f1:" + f1); System.out.println("f2:" + f2); if (f1 < f2) { System.out.println("f1<f2 成立"); } else { System.out.println("f1<f2 不成立"); } float f3 = 30000000f; float f4 = f3 + 1; System.out.println("f3:" + f3); System.out.println("f4:" + f4); if (f3 < f3) { System.out.println("f3<f3 成立"); } else { System.out.println("f3<f4 不成立"); } } }
运行的结果:
f1:30000.0 f2:30001.0 f1<f2 成立 f3:3.0E7 f4:3.0E7 f3<f4 不成立
一个数加一不比原来的数大,这就是由于精度产生的现象了。
例5
float可以保留7-8位有效数字
int a = 1; int b = 123; int c = 1234; int d = 12345; int e = 123456; int f = 1234567; int g = 12345678; int h = 123456789; int i = 1234567891; long j = 12345678912L; long k = 123456789123L; long l = 1234567891234L; long m = 12345678912345L; System.out.println((float)a); System.out.println((float)b); System.out.println((float)c); System.out.println((float)d); System.out.println((float)e); System.out.println((float)f); System.out.println((float)g); System.out.println((float)h); System.out.println((float)i); System.out.println((float)j); System.out.println((float)k); System.out.println((float)l); System.out.println((float)m);
结果为:
1.0 123.0 1234.0 12345.0 123456.0 1234567.0 1.2345678E7 1.23456792E8 1.23456794E9 1.23456788E10 1.23456791E11 1.23456795E12 1.2345679E13
double类型可以保留15-16位有效数字
System.out.println((double)a); System.out.println((double)b); System.out.println((double)c); System.out.println((double)d); System.out.println((double)e); System.out.println((double)f); System.out.println((double)g); System.out.println((double)h); System.out.println((double)i); System.out.println((double)j); System.out.println((double)k); System.out.println((double)l); System.out.println((double)m);
结果为:
1.0 123.0 1234.0 12345.0 123456.0 1234567.0 1.2345678E7 1.23456789E8 1.234567891E9 1.2345678912E10 1.23456789123E11 1.234567891234E12 1.2345678912345E13
个人猜想:
强制类型转换如long到int,是截取高位的,一个正的long有可能转成一个负的int类型。
而整型到浮点型的转换是忽略低位的精度,相对说比较稳定。
因此前者需要强制类型转换,而后者可以进行自动类型转换。