网站首页 > 技术文章 正文
在初学Java的时候,一般我们都会从基本的数据类型开始学习,而在基本数据类型中,我认为double类型是比较难理解的,并且在以后的学习或工作中,在double类型数据这遇到的坑也是极多的。例如下面的这样一个程序
public static void main(String[] args) { System.out.println(2.0-1.1); }
很多人会认为上面的程序会打印出0.9,但实际上,它打印的却是0.8999999999999999,这是为什么呢?
首先介绍一下,十进制小数是怎么转换为二进制数的,举个例子3.75
首先取出3.75的小数部分0.75,将其乘以要转换的进制的进制数,在这里也就是2,乘以2后得到结果1.5,取1.5的整数部分作为二进制小数的小数部分的第一位,再取1.5的小数部分0.5,乘以2后得到1.0,将1.0的整数部分1作为小数部分的第二位。以此类推,直到最后得到值0或形成无限循环。3.75转换为二进制数就是11.11;
但是我们知道在Java中double类型的数据占8个字节,所以对于无限循环的二进制小数我们只能取到它的近似值,就比如说1.1。
下面自己写了一个将double类型的数转换为二进制的程序
public static void main(String[] args) { doubleToBinary(3.75000000); doubleToBinary(173.8125); doubleToBinary(1.10); } /** * 10任何次负幂都不能精确地被表示为一个长度有限的二进制数 * @param d */ public static void doubleToBinary(double d) { // 获得double类型的整数部分 int intPart = (int) d; String tempStr = d + ""; // 获得double类型数的字符串形式的小数部分 String decimalPartStr = tempStr.substring(tempStr.indexOf(".")); BigDecimal decimal = new BigDecimal(decimalPartStr); // 获得小数点后面的位数 int precision = decimal.precision(); // 最终的小数部分二进制字符串 String decimalPartBinary = decimalPartToBinary(Double.parseDouble(decimalPartStr), precision); System.out.println(Integer.toBinaryString(intPart) + "." + decimalPartBinary); } /** * 将小数部分转换为二进制字符串 * @param decimalPart 小数部分 * @param precision 原始数的小数部分位数 * @return */ public static String decimalPartToBinary(double decimalPart, int precision) { // 转换为整型 long decimalPartLong = (long) (Math.pow(10, precision) * decimalPart); String temp = ""; int i = 0; while (precision > 0 && i < 64) { decimalPartLong = decimalPartLong * 2; temp += (int) (decimalPartLong / Math.pow(10, precision)); //取得除第一位之后的数,并转换为字符串 String str = (long) (decimalPartLong % Math.pow(10, precision)) + ""; if (str.charAt(str.length() - 1) == '0' && str.length() != 1) { //去掉数最后面的0 decimalPartLong = Long.parseLong(str.substring(0, str.length() - 1)); } else { decimalPartLong = Long.parseLong(str); } if (decimalPartLong == 0) { break; } ++i; precision = (decimalPartLong + "").length(); } return temp; }
上面程序中的main方法输出的值为
11.11 10101101.1101 1.0001100110011001100110011001100110011001100110011001100110011001
通过上面的程序,我们很容易看到测试数据1.10在小数部分是无限循环的,1.10并不能精确地表示为一个double,因此它在Java中被表示为最接近它的double值。既然Java中是对double不能精确表示的数以近似值去存储的,那么在一些需要精确计算的地方就有可能出现错误,甚至产生意想不到的结果。比如说在业务中经常碰到的货币计算。那么如何解决这个问题呢?
1.使用执行精确小数运算的BigDecimalAPI,但这里要说明一点,最好(一定)要用BigDecimal(String val)构造方法,而不要使用BigDecimal(double val),因为BigDecimal(double val)构造方法将会使用它的参数val的精确值返回一个BigDecimal,比如new BigDecimal(1.1)将会返回一个表示1.100000000000000088817841970012523233890533447265625的BigDecimal。
2.使用整数类型进行计算,比如说int或long。将2.0元转换为200分去计算,这样System.out.println((200-110)+ "分")将会打印出正确的结果90分。
总而言之呢,在需要精确计算的地方要避免使用float和double的数据。
猜你喜欢
- 2024-09-29 “全栈2019”Java原子操作第六章:AtomicInteger灵活的运算方式
- 2024-09-29 JAVA程序设计练习题一(附答案)(java程序设计题目题库)
- 2024-09-29 Java基础笔试练习(四)(java基础笔试题大全带答案)
- 2024-09-29 C/C++实战030:double转int常见的取整操作
- 2024-09-29 java-数据类型转换(Java数据类型转换规则)
- 2024-09-29 Java String字符串和整型int的相互转换
- 2024-09-29 Java教程:任意大整数的相加运算(java编程实现任意两个整数的大小)
- 2024-09-29 Java修炼终极指南:20-21. double和整数
- 2024-09-29 在java中String的方法:toString()怎么用
- 2024-09-29 Java基础之String与int两者之间如何相互转换?
- 1509℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 530℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 493℃MySQL service启动脚本浅析(r12笔记第59天)
- 473℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 470℃启用MySQL查询缓存(mysql8.0查询缓存)
- 451℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 430℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 427℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- chromepost (65)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- linux删除一个文件夹 (65)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)