01 JAVA 基础补充 浮点数的比较

来源:互联网 发布:php微信第三方登录api 编辑:程序博客网 时间:2024/05/18 17:05

这篇文章主要记录一些关于精度问题的小知识,当然也不知道对不对大哭,先记录一下~啦啦啦~我是勤劳的屌丝程序猿 少年难过。。


顺便复习:原码,反码,补码对于正数来说,都是一样的,对于负数,反码除符号位之外都取反,补码是在反码的基础上最低位+1,移码就是补码,但是符号位取反

0.1+0.2 == 0.3 返回false, WHY???

1. 浮点数的存储格式

对于float: 符号位S(1)阶码E(8)尾数M(23)

阶码表示相对于127的偏移量,等于127为0,大于127为负数,大于127为正数,比如10000011,131-127=4,偏移量的K值为2**4=16

N=(-1)**S*1.M*2**偏移位


例1:1 10000010 1001000 00000000 00000000

S=1,说明该数是负数

EEEEEEEE=10000010,偏移量K=2**(130-127)=2**3

M=1001000 00000000 00000000

在尾数的左边有一个省略的小数点和1,这个1在浮点数的保存中常常省略,在尾数M的开头加上一个1和小数点,变成

1.M=1.1001000 00000000 00000000

(1.M)*2**3=1100.1000 00000000 00000000

(8+4).(1/2)

我们可以计算出N=12.5


例2:12345

数据化成二进制为11000000111001-----> 1.1000000111001*2^13

S=0

EEEEEEEE=13+127=140

M=1000000111001


例3:123.456

二进制为1111011.01110100101111001

S=0

EEEEEEEE=6+127=133

M=11101101110100101111001


对于尾数来说,有些小数并不能用m1*1/(2**n1)+m2*1/(2**n2)+...这样的式子完整的表示出来,如0.2=0.125+0.0625+...,此时我们只能截取前面几位位,所以换算回十进制的时候,不能变回0.2了。对于double类型来说也存在一样的问题,只是E和M的个数发生了改变~


例4:2.2

用科学计数法表示应该将十进制的小数转换为二进制的小数的方法为将小数*2,取整数部分,所以0.2*2=0.4,取0,接着0.4*2=0.8,取0,0.8*2=1.6,取1,0.6*2=1.2取1,keep doing this,永远都不能相乘得到1.0,得到的一个二进制是无限循环的排列00110011001100110011...,此时2.2的float存储为:

10.001100110011001100110011...

1.00011001100110011001100*2**1

S=0

EEEEEEEE=127+1=128

M=00011001100110011001100

0 10000000 00011001100110011001100


2. 精度问题的解决

- epsilon

1) 引入epsilon绝对误差

if (fabs(s1-s2)<epsilon)

  • 加入epsilon如果是0.00001,而是s1和s2正好在0.00001附近时就比较不出来了,比如10000与10000.000009
  • 10000与10000.000977只有最后一位二进制码不同,但是epsilon不能判断是否可以用,起不了作用

2)引入epsilon相对误差

如果epsilon是0.000001对于附近的数显得太大了,对于10000附近的数显得太小,此时我们可以使用精度百分率fabs((result-expectedResult)/expectedResult)与epsilon进行比较

保证isequal(x1,x2,epsilon)和isequal(x2,x1,eplison)返回结果一样


如果两个是接近于0,但符号相反,绝对值可能很大啊,所以我们还可以用线段距离来判断

3)BigDecimal

关于BigDecimal,1个符号位,M位表示二进制(任意长度),N为表示10进制(32位)

例:M=96,N=28

M*10**(-N)

差不多4个二进制-》1个十进制

96/4=24


import java.math.BigDecimal;// Decimal是不可变的,每一步运算,都会产生新的对象public class testBigDecimal {public static void main(String args[]) {BigDecimal ad = new BigDecimal(1.22);System.out.println("a double value: "+ad);BigDecimal adouble = new BigDecimal(Double.toString(1.22));System.out.println("a double value: "+adouble);BigDecimal astring = new BigDecimal("1.22");System.out.println("a string value: "+astring);astring.add(adouble);System.out.println("result: "+astring);astring=astring.add(adouble);System.out.println("result: "+astring);}}

结果:

a double value: 1.2199999999999999733546474089962430298328399658203125   //原因是实参1.22被double形式存储,然后再取出来

a double value: 1.22

a string value: 1.22

result: 1.22

result: 2.44


4)有时候,别忘了精度函数其实也可以忙上一点忙

Math.round(x)表示四舍五入,将x+0.5后,向下取整

Math.ceil(x)表示不小于x的最小整数

Math.floor(x)表示不大于x的最大整数


Reference:

http://blog.csdn.net/hziee_/article/details/1477427

http://blog.csdn.net/rsp19801226/article/details/3085343

http://blog.csdn.net/jackiehff/article/details/8582449

http://www.cnblogs.com/herbert/p/3402245.html

http://blog.csdn.net/quickbasic411/article/details/5921420




0 0
原创粉丝点击