首页 > 编程语言 >java 将小数拆分为两部分+浮点型精度丢失问题

java 将小数拆分为两部分+浮点型精度丢失问题

时间:2022-12-13 13:01:51浏览次数:50  
标签:java 二进制 float System 整数 浮点 println 小数

问题:将一个String类型的小数拆分为整数部分和小数部分,如9.9拆分为9和0.9

1.将小数的整数和小数部分拆分开

public float numberSub(String totalMoney){
float moneyFloat=Float.parseFloat(totalMoney);
System.out.println("moneyFloat="+moneyFloat);
int moneyInteger=(int) moneyFloat;
System.out.println("moneyInteger="+moneyInteger);
float moneyDecimal=moneyFloat-moneyInteger;
System.out.println("moneyDecimal="+moneyDecimal);
float result=this.sub(moneyFloat, moneyInteger);
return result;
}

上面这个方法里面,float-->int转化时直接丢弃小数部分,从而取得小数中的整数,而后作差得到小数部分,但是看下面输出:

java 将小数拆分为两部分+浮点型精度丢失问题_System

2.浮点型表示一个小数的时候存在精度不准确的问题

 原因:

首先我们要搞清楚下面两个问题:  
(1) 十进制整数如何转化为二进制数
算法很简单。举个例子,11表示成二进制数:
11/2=5 余 1

5/2=2 余 1

2/2=1 余 0

1/2=0 余 1

0结束 11二进制表示为(从下往上):1011

这里提一点:只要遇到除以后的结果为0了就结束了,大家想一想,所有的整数除以2是不是一定能够最终得到0。换句话说,所有的整数转变为二进制数的算法会不会无限循环下去呢?绝对不会,整数永远可以用二进制精确表示 ,但小数就不一定了。

(2) 十进制小数如何转化为二进制数
算法是乘以2直到没有了小数为止。举个例子,0.9表示成二进制数
0.9*2=1.8 取整数部分 1

0.8(1.8的小数部分)*2=1.6 取整数部分 1

0.6*2=1.2 取整数部分 1

0.2*2=0.4 取整数部分 0

0.4*2=0.8 取整数部分 0

0.8*2=1.6 取整数部分 1

0.6*2=1.2 取整数部分 0

......... 0.9二进制表示为(从上往下): 1100100100100......

注意:上面的计算过程循环了,也就是说*2永远不可能消灭小数部分,这样算法将无限下去。很显然,小数的二进制表示有时是不可能精确的 。其实道理很简单,十进制系统中能不能准确表示出1/3呢?同样二进制系统也无法准确表示1/10。这也就解释了为什么浮点型减法出现了精度丢失的问题。

3.验证

众所周知、 Java 的float型在内存中占4个字节。float的32个二进制位结构如下

java 将小数拆分为两部分+浮点型精度丢失问题_System_02

float内存存储结构   

 4bytes          31                  30                29----23          22----0         

 表示       实数符号位      指数符号位        指数位          有效数位

 其中符号位1表示正,0表示负。有效位数位24位,其中一位是实数符号位。

将一个float型转化为内存存储格式的步骤为:

     (1)先将这个实数的绝对值化为二进制格式,注意实数的整数部分和小数部分的二进制方法在上面已经探讨过了。 
     (2)将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边。 
     (3)从小数点右边第一位开始数出二十三位数字放入第22到第0位。 
     (4)如果实数是正的,则在第31位放入“0”,否则放入“1”。 
     (5)如果n 是左移得到的,说明指数是正的,第30位放入“1”。如果n是右移得到的或n=0,则第30位放入“0”。 
     (6)如果n是左移得到的,则将n减去1后化为二进制,并在左边加“0”补足七位,放入第29到第23位。如果n是右移得到的或n=0,则将n化为二进制后在左边加“0”补足七位,再各位求反,再放入第29到第23位。

我们以数字8举例验证,float类型为8.0

1.将8.0转换为二进制之后是1000.0

2.将小数点左移三位到第一个有效位右侧1.0000(保证有效位数24位)得1.00000000000000000000000(只保留24位,多出的被截取掉了,从而引起了误差!)

3.这时已经有了二十四位有效数字,将最左边一位“1”去掉,得到0000000000000000000000共23位,将它放入float存储结构的第22到第0位。

4.因为8.0是正数,因此在第31位实数符号位放入“0”。

5.由于我们把小数点左移,因此在第30位指数符号位放入“1”。

6.因为我们是把小数点左移3位,因此将3减去1得2,化为二进制并补足7位得到0000010,放入第29到第23位。

最后得到0 1 0000010 0000000000000000000000

代码中打印出来如下:

public void testJavaDataType(){
int aint=8;
float afloat=8;
int fl=Float.floatToIntBits(afloat);
double adouble=8;
long dl=Double.doubleToLongBits(adouble);
System.out.println("Int 8 :"+Integer.toBinaryString(aint));
System.out.println("Float 8 :"+Integer.toBinaryString(fl));
System.out.println("Double 8 :"+Long.toBinaryString(dl));
}

java 将小数拆分为两部分+浮点型精度丢失问题_System_03

打印的时候如果是整数则第31位0默认不打印,篮框是第30位指数符号位,红框是指数位,蓝底是有效位数

很显然对于小数来说一个有限的23位有效位数是不足以精确表示一个小数的。

4.BigDecimal解决

public float sub(float a,float b){
BigDecimal bda=new BigDecimal(Float.toString(a));
BigDecimal bdb=new BigDecimal(String.valueOf(b));
return bda.subtract(bdb).floatValue();
}

 5.利用String截取小数点来解决该问题

public float stringSub(String totalMoeny){
String[] strArray=totalMoeny.split("\\.");
if(strArray.length>=2){
float a=Float.valueOf(strArray[0]);
System.out.println(a);
float b=Float.parseFloat("0."+strArray[1]);
System.out.println(b);
return a-b;
}else{
return 0l;
}
}

 



java 将小数拆分为两部分+浮点型精度丢失问题_System_04

作者:​​翎野君​


若本文如对您有帮助,不妨点击一下右下角的。


如果您喜欢或希望看到更多我的文章,可扫描二维码关注我的微信公众号《翎野君》。


转载文章请务必保留出处和署名,否则保留追究法律责任的权利。



标签:java,二进制,float,System,整数,浮点,println,小数
From: https://blog.51cto.com/lingyejun/5933835

相关文章

  • Java方法
    方法方法的定义方法就是一段可重复调用的代码段,例如:有某段长度约100行的代码,要在多个地方使用此段代码,如果在各个地方都重复编写此部分代码的话,则肯定会比较麻烦,而且此部......
  • pom之 数据源信息 spring-boot-starter-jdbc ,mysql-connector-java
    <!--    数据源信息--><!--    <dependency>--><!--      <groupId>org.springframework.boot</groupId>--><!--      <arti......
  • [Java EE] java.net.SocketException: Connection reset【解决中】
    1错误描述6:44:33.112][DEBUG][http-nio-9527-exec-3][HttpClientUtil]httpposturl:http://bdp-gateway-service-parent-backend-service.bigdata/bdp/public/api/......
  • Java 中的抽象介绍
    这篇文章主要介绍了Java 中的抽象,数据抽象是一种仅向用户显示基本细节的属性。不向用户显示琐碎或非必需的单元,下面文章Java抽象详细内容,需要的朋友可以参考一下目录1......
  • idea 自带java 反编译工具问题
    就在最近升级到idea最新版本,发现以前java-decompiler命令行工具不能使用了,运行会有错误解决方法找到一个地版本的idea拷贝java-decompiler.jar直接替换,或者直接使用......
  • Java程序员除了做增删改查还能干嘛?
      就以Java后端开发为例,说说不同级别程序员干的事情。1初级开发,大概是有3年Java开发经验。  22年底,上海,这批程序员如果学历是本科,薪资一般是8k到2w,当然如果能进......
  • JAVA操作PDF实现简单盖章功能(未签字)
    默认再第一页签章:https://www.cnblogs.com/wolf-shuai/p/16977802.html摘要:jar包准备:bcpkix-jdk15on-1.70.jarbcprov-jdk15on-1.70.jariTextAsian.jaritextpdf-5.5.1......
  • 【java-01】springboot利用sharding jdbc实现读写分离
    写在开头打算把自己的java后端学习过程分享给大家,也方便之后自己回顾。从这里开始~目前在学习黑马的瑞吉外卖新手入门项目,这篇随笔记录的是项目优化之一读写分离先列出......
  • Java POI 常用操作
    POI常用操作importorg.apache.poi.ss.usermodel.*;importorg.apache.poi.ss.util.CellRangeAddress;importorg.apache.poi.xssf.usermodel.XSSFCell;importorg.ap......
  • Java数组(1)
        ......