首页 > 编程语言 >[Java] 浮点数的精度丢失问题与精度控制方法

[Java] 浮点数的精度丢失问题与精度控制方法

时间:2024-11-19 11:10:45浏览次数:1  
标签:Java BigDecimal double 浮点数 number2 number1 精度

1 需求描述

  • 场景1:两个整型相除,如何保证运算结果为浮点数?如何控制运算结果的精度(小数位数)?
  • 场景2:针对一个浮点数,如何控制其精度(小数位数)?

2 试验

场景:两整型数相除,控制运算结果、浮点数的精度

Long number1 = 110600L;
int number2 = 999;
int scale = 2;//精度n(保留n位小数)

// 【错误示范】
//方式1
double i = number1/number2; // 110.0 (结果错误,精度丢失)
//方式2
double i = (double) (number1/number2) // 110.0 (结果错误,精度丢失)

//方式3
BigDecimal bd1 = BigDecimal.valueOf(number1);
BigDecimal bd2 = BigDecimal.valueOf(number2);
//(可能)将报错: java.lang.AthmeticException : Non-terminating decimal expansion; no exact representable decimal result.()
// java.lang.AthmeticException : 非终止小数展开;没有可精确表示的十进制结果 (意译: 这个除法除不尽,会得到一个无限小数,导致异常。)
BigDecimal bd3 = bd1.divide(bd2).setScale( scale, RoundingMode.HALF_UP);


//【正确示范】不丢失精度
//方式4 转换其中至少1个整型为浮点型 | 利用java运算时,运算结果类型默认等于精度最高的类型
double number3 = (double) number1/number2; //等效于 : ((double) number1)/number2 // 110.71071071071071 (结果正确,精度未丢失)




//方式5 BigDecimal + BigDecimal.divide(被除数, 精度n:=保留n位小数, 向上入|向下舍)
BigDecimal bd1 = BigDecimal.valueOf(number1);
BigDecimal bd2 = BigDecimal.valueOf(number2);
BigDecimal bd3 = bd1.divide(bd2 , scale, RoundingMode.HALF_UP);//解决方式3的异常情况
bd3.doubleValue();//输出为浮点数 : 110.71d
bd3.toString();//输出为字符串 : "110.71"

DecimalFormat df = new DecimalFormat("#0.0000");
String dfResult = df.format(bd3);//格式化浮点数 : "110.7100"
String dfResult = df.format(110.32535235);// "110.3254" (默认会四舍五入)

3 总结

场景1:2个整数相除,计算结果默认为整型,导致精度丢失问题

方法1:至少将其中一个整型转为浮点数,则:运算结果一定是浮点数

方法2:利用 BigDecimal + BigDecimal.divide(被除数, 精度n:=保留n位小数, 向上入|向下舍)

场景2:如何对浮点数进行四舍五入运算?

方法1: Math.round(number) + Math.pow(10, scale) + 乘法、除法运算

Math.round() 方法可以将浮点数四舍五入到最接近的整数。
如果你需要四舍五入到特定的小数位数:

  • 首先,可以先将数字乘以10的n次方(n为你想要保留的小数位数)
  • 然后,使用Math.round()进行四舍五入,最后再除以10的n次方得到结果。
double number3 = (double) number1/number2;

long x = Math.round( number3 * Math.pow(10, scale)  );
double y = Math.pow(10, scale); //运算结果为浮点型
double result = x/y;//110.71

方法2: BigDecimal(number).setScale(scale, BigDecimal.ROUND_HALF_UP)

double number3 = (double) number1/number2;

BigDecimal result = new BigDecimal(number3).setScale(scale, BigDecimal.ROUND_HALF_UP);
result.doubleValue(); // 110.71
result.toString();//"110.71"

如果目标浮点数是除法运算后的结果,还可在运算过程中利用BigDecimal.divide(被除数, 精度n:=保留n位小数, 向上入|向下舍)方法,直接在运算时控制运算结果的精度:

//核心思路: BigDecimal + BigDecimal.divide(被除数, 精度n:=保留n位小数, 向上入|向下舍)

BigDecimal bd1 = BigDecimal.valueOf(number1);
BigDecimal bd2 = BigDecimal.valueOf(number2);
BigDecimal bd3 = bd1.divide(bd2 , scale, RoundingMode.HALF_UP);
bd3.doubleValue();//输出为浮点数 : 110.71d
bd3.toString();//输出为字符串 : "110.71"

DecimalFormat df = new DecimalFormat("#0.0000");
String dfResult = df.format(bd3);//格式化浮点数 : "110.7100"
String dfResult = df.format(110.32535235);// "110.3254" (默认会四舍五入)

方法3:String.format("%.2f")

虽然String.format()方法主要用于格式化字符串,但它也可以用于四舍五入浮点数到指定的小数位数。
该方法不直接改变数字,而是将其格式化为包含指定小数位数的字符串。

double number3 = (double) number1/number2;//110.71071071071071

String s = String.format("%.2f", number3); // "110.71"

double d = Double.parseDouble(s); // 110.71d

注意,使用String.format()方法时,结果是一个字符串,如果你需要将其作为浮点数进行进一步操作,可以使用Double.parseDouble()将其转换回double类型。

方法4:使用DecimalFormat

  • java.text.DecimalFormatNumberFormat的一个具体子类,用于格式化十进制数。它允许你为数字、整数和小数指定模式。
double number3 = (double) number1/number2;//110.71071071071071

DecimalFormat decimalFormat = new DecimalFormat("#.##");//保留2位小数
String s = decimalFormat.format(number3);//"110.71"

Double d = Double.parseDouble(s); // 110.71d

String.format()类似,DecimalFormat的结果也是一个字符串,可以通过Double.parseDouble()转换回double类型。

X 参考文献

标签:Java,BigDecimal,double,浮点数,number2,number1,精度
From: https://www.cnblogs.com/johnnyzen/p/18554455

相关文章

  • 记录个Java/Groovy的小问题:空字符串调用split函数返回非空数组
    问题复现最近写了一个groovy替换程序增量流水线脚本(会Java也能看懂),示意脚本如下://获取文件列表方法deflistFiles(folder){defoutput=sh(script:"ls${folder}",returnStdout:true).trim()returnoutput.split('\n')asList}//调用以上方法获取lib目录下......
  • JAVA反序列化学习-CommonsCollections3(基于ysoserial)
    环境准备JDK1.7(7u80)、commons-collections(3.x4.x均可这里使用3.2版本)JDK:https://repo.huaweicloud.com/java/jdk/7u80-b15/jdk-7u80-windows-x64.exe<dependency><groupId>commons-collections</groupId><artifactId>commons-collections</a......
  • 《探索 Java 代码的魅力》
    在编程的世界里,Java无疑是一颗璀璨的明星。它以其强大的功能、跨平台性和广泛的应用场景,成为了众多开发者的首选语言。一、Java的历史与发展Java诞生于1995年,由SunMicrosystems公司推出。它的设计初衷是为了开发一种可以在各种不同的设备上运行的编程语言,以实现“......
  • 《深入理解 Java 类》
    在Java编程中,类(Class)是构建程序的基本单元。它封装了数据和操作这些数据的方法,为代码的组织和复用提供了强大的机制。一、Java类的定义一个Java类通常由以下几个部分组成:类声明:使用关键字“class”来声明一个类,后面跟着类名。例如:classMyClass{}。成员变量:也称......
  • Java 框架:强大的开发利器
    在当今的软件开发领域,Java一直以其强大的性能、跨平台性和丰富的生态系统而备受青睐。而Java框架更是在开发过程中发挥着至关重要的作用,它们为开发者提供了高效、可维护和可扩展的开发方式。一、什么是Java框架?Java框架是一种预先编写好的代码库和工具集合,旨在帮助......
  • JAVA 继承:代码复用与功能扩展的强大机制
    在Java编程中,继承是一种非常重要的概念,它为代码复用和功能扩展提供了强大的机制。本文将深入探讨Java继承的概念、用法以及其带来的好处。一、什么是Java继承?继承是面向对象编程中的一个重要概念,它允许一个类(子类)继承另一个类(父类)的属性和方法。子类可以扩展父类的......
  • java_1
    目录学习内容:一:变量,数据类型,运算符1.变量 2.数据类型数据类型详解:3.常量 4.运算符算数运算符:关系运算符: 位运算符:逻辑运算符:  赋值运算符: 5.Java运算符优先级: 6.Scanner的用法学习内容:一:变量,数据类型,运算符1.变量变量:变量可以变化,是放在内存中运行......
  • java日志常用配置
    <!--pom.xml--><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.30</version></dependency><dep......
  • JAVA WEB 实现文件夹上传(保留目录结构)分享
    需求:大文件上传,批量上传,断点续传,文件夹上传,大文件下载,批量下载,断点下载,文件夹下载文件夹:上传下载需要支持层级结构,采用非压缩方式文件大小:100G前端:vue2,vue3,vue-cli,jquery,html,webuploader后端:JSP,springbootweb服务:tomcat数据库:mysql,oracle,达梦,国产化数据库服务......
  • Java运行环境的安装与配置 idea的安装
    安装Java的运行环境后,对于开发人员来说,只需要编写一次Java源码,就可以在任何支持Java的平台上运行,就不需要关心平台的差异和编译问题,从而大大的提高了开发效率。、一配置Java开发环境步骤1.下载JDKhttps://www.oracle.com/java/technologies/downloads/在官方网站......