首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > JAVA > J2SE开发 >

精度有关问题讨论

2012-01-26 
精度问题讨论当作散分吧今天一直在想这个问题不明白问什么会有奇怪的结果找了书,查了资料深入理解计算机系

精度问题讨论
当作散分吧

今天一直在想这个问题
不明白问什么会有奇怪的结果
找了书,查了资料
深入理解计算机系统都搬出来看了
想了很多道理都不能解释的通

                System.out.println(1.20-1.10);
                System.out.println(1.30-1.10);
                System.out.println(1.40-1.10);
                System.out.println(1.50-1.10);
                System.out.println(1.60-1.10);
                System.out.println(1.70-1.10);
                System.out.println(1.80-1.10);
                System.out.println(1.90-1.10);
                System.out.println(2.00-1.10);
                System.out.println(2.10-1.10);

打印结果:
0.09999999999999987
0.19999999999999996
0.2999999999999998
0.3999999999999999
0.5
0.5999999999999999
0.7
0.7999999999999998
0.8999999999999999
1.0

按照官方解释是说1.10这个数字不能被精确表示成为一个double,因此它被表示成为最接近它的double值(从而会造成一堆数字)

我接受了,但是大家可以看到上面的结果是0.5,0.7和1.0都还是很好的结果
而且我在想其实像1.10这样的数字实在是太多了,从统计上来讲有一大半都是不能被有限二进制表示的,难道这些都要造成这么复杂的结果?

关键是这里打印出来了0.5,0.7和1.0
经过实验,人为造出了这些数字的有一串的形式,列举如下:
                System.out.println(2.20-1.70);
                System.out.println(2.20-1.50);
                System.out.println(2.20-1.20);
                System.out.println(1.0000000000000003);

打印结果如下:
0.5000000000000002
0.7000000000000002
1.0000000000000002
1.0000000000000002

从而消除了0.5,0.7和1.0是因为可以被有限表示而有很好结果的可能
(最后一个是该数的最近表示,作为参考)

我暂时的问题就在这里了
假如说是1.10的问题,为什么0.5,0.7和1.0却能够出现?

欢迎大家讨论~~

[解决办法]
好简单,因为数在计算机内部表示为二进制数,这点lz的深入理解计算机系统应该有说明。
二进制转换成十进制很多时候是不能精确转换的,
当然也有例外,如1.60-1.10=0.5.
但为何2.20-1.70!=0.5呢?那大概是2.2或者1.7转化成二进制时失去了精度所导致的

[解决办法]
帮顶一下
[解决办法]
帮顶
[解决办法]

[解决办法]
看3楼的回复。
[解决办法]
我来说两句~
首先是:2.20 - 1.70
把2.20转成2进制:
0 10000000000 0001100110011001100110011001100110011001100110011010
2进制科学记数法表示:
1.0001100110011001100110011001100110011001100110011010 * 2的1次幂
(指数位是10000000000,因为有127的偏移量,所以就是1次幂)

1.70的2进制
0 01111111111 1011001100110011001100110011001100110011001100110011
2进制科学记数法表示:
1.1011001100110011001100110011001100110011001100110011 * 2的0次幂

做减法时先把幂次对齐
10.0011001100110011001100110011001100110011001100110100
1.1011001100110011001100110011001100110011001100110011
-------------------------------------------------------
0.1000000000000000000000000000000000000000000000000001
换下写法:
1.0000000000000000000000000000000000000000000000000010 * 2的-1次幂
就是:
0 01111111110 0000000000000000000000000000000000000000000000000010



来看看0.5000000000000002的2进制表示:
0 01111111110 0000000000000000000000000000000000000000000000000010
而0.5000000000000001的2进制表示
0 01111111110 0000000000000000000000000000000000000000000000000001
而0.5的2进制表示
0 01111111110 0000000000000000000000000000000000000000000000000000

当然在2.20和1.70转二进制的时候就已经出现误差了,
只能说2.20 "对应 "
0 10000000000 0001100110011001100110011001100110011001100110011010
但是至少在同一个环境中不论什么时间2.20都是对应这个二进制值的.
同样这个0.5000000000000002也有着它对应的二进制值,
反过来这个二进制也应该对应0.5000000000000002

0 01111111110 0000000000000000000000000000000000000000000000000010
这个二进制的值是可以用十进制精确表示出来的,但是为什么只截取到0.5000000000000002呢?
我想是因为即使是用精确的值表示也是没有意义的,因为这个结果本身就是不精确的.
只需要截取到一定位数使得这个十进制的数能唯一与这个二进制数对应就可以了.

再下面看一下:2.10 - 1.10
2.10对应二进制:
0 10000000000 0000110011001100110011001100110011001100110011001101
也就是
1.0000110011001100110011001100110011001100110011001101 * 2的1次幂
10.0001100110011001100110011001100110011001100110011010 * 2的0次幂

1.10对应二进制:
0 01111111111 0001100110011001100110011001100110011001100110011010
也就是
1.0001100110011001100110011001100110011001100110011010 * 2的0次幂

相减:
10.0001100110011001100110011001100110011001100110011010
1.0001100110011001100110011001100110011001100110011010
-------------------------------------------------------
1.0000000000000000000000000000000000000000000000000000

这么多0和1眼都花了....
虽然上面说的没什么依据,但至少我觉得还说的通~
[解决办法]
有一本叫作《Java数值方法》(Java Number Cruncher: The Java Programmer 's Guide to Numerical Computing)的书中第一章就很详细地讲述了浮点数运算的精度问题。在这里推荐一下,很不错的一本书。
[解决办法]
如果两个都是偏小误差,而且相减的结果可以准确表示,就是准确的结果;
如果一个是偏小误差另一个不是,结果就是近似的:或多或少,看偏小的做被减数还是减数了
[解决办法]
不知道是不是我调试错误,测试如下:
BigDecimal b1=new BigDecimal(1.20-1.10);
System.out.println(b1);

double b2=1.20-1.10;
System.out.println(b2);

float b3=(float) (1.20-1.10);
System.out.println(b3);

输入结果如下:
0.0999999999999998667732370449812151491641998291015625
0.09999999999999987
0.1
[解决办法]
连java中的基本精度计算都不懂!
所有的加减乘除计算都要用BigDecimal!!!不然银行系统早就趴下了!
[解决办法]
楼上怎么连基本的素质都没有?
先看清楼主讲的什么吧!!!
[解决办法]
我当然知道,楼上的也麻烦看一下标题。
先道个歉,上面的语气确实不太好,Sorry。不过我碰到过太多的人大谈java精度问题,我是觉得没有这个必要谈论。只要牢记一点就好,在java中所有涉及到大数及高精度的运算,请使用BigDecimal,并且BigDecimal的参数,必须是String类型,希望baobao28(瓜瓜) 也知道。Sun官方也明确在文章和书籍中指出:float,double之类的运算只适合在科学计算和工程计算时使用,在商业计算中要使用BigDecimal。
要知道float,double运算后为什么会有偏差,请先看一下这篇基础文章:
http://java.ccidnet.com/art/3737/20060626/589429_1.html
概况一句话就是:not all numbers can be represented with a certain amount of bits!This is a fundamental limitation of digital computers
[解决办法]
up ~~

热点排行