JAVA浮点数计算精度损失底层原理与解决方案
浮点数会有精度损失这个在上大学的时候就已经被告知,但是至今完全没有想明白其中的原由,老师讲的时候也是一笔带过的,自己也没有好好琢磨。终于在工作的时候碰到了,于是google了一番。
问题:
对两个double类型的值进行运算,有时会出现结果值异常的问题。比如:
1 System.out.println(19.99+20);
2 System.out.println(1.0-0.66);
3 System.out.println(0.033*100);
4 System.out.println(12.3/100);
输出:
39.989999999999995
0.33999999999999997
3.3000000000000003
0.12300000000000001
Java中的简单浮点数类型float和double不能够精确运算。这个问题其实不是JAVA的bug,因为计算机本身是二进制的,而浮点数实际上只是个近似值,所以从二进制转化为十进制浮点数时,精度容易丢失,导致精度下降。
关于精度损失的原理可以很简单的讲,首先一个正整数在计算机中表示使用01010形式表示的,浮点数也不例外。
比如11,11除以2等于5余1
5除以2等于2余1
2除以2等于1余0
1除以2等于0余1
所以11二进制表示为:1011.
double类型占8个字节,64位,第1位为符号位,后面11位是指数部分,剩余部分是有效数字。
正整数除以2肯定会有个尽头的,之后二进制还原成十进制只需要乘以2即可。
举个例子:0.99用的有效数字部分,
0.99 * 2 = 1+0.98 --> 1
0.98 * 2 = 1+0.96 --> 1
0.96 * 2 = 1+0.92 -- >1
0.92 * 2 = 1+0.84 -- >1
...............
这样周而复始是没法有尽头的,而double有效数字有限,所以必定会有损失,所以二进制无法准确表示0.99,就像十进制无法准确表示1/3一样。
解决办法:
在《Effective Java》中提到一个原则,那就是float和double只能用来作科学计算或者是工程计算,但在商业计算中我们要用java.math.BigDecimal,通过使用BigDecimal类可以解决上述问题,首先需要注意的是,直接使用字符串来构造BigDecimal是绝对没有精度损失的,如果用double或者把double转化成string来构造BigDecimal依然会有精度损失,所以我觉得这种解决方法就是在使用中就把浮点数用string来表示存放,涉及到运算直接用string构造double,否则肯定会有精度损失。
1. 相加
/**
* 相加
* @param double1
* @param double2
* @return
*/
public static double add(String doubleValA, String doubleValB) {
BigDecimal a2 = new BigDecimal(doubleValA);
BigDecimal b2 = new BigDecimal(doubleValB);
return a2.add(b2).doubleValue();
}
2. 相减
/**
* 相减
* @param double1
* @param double2
* @return
*/
public static double sub(String doubleValA, String doubleValB) {
BigDecimal a2 = new BigDecimal(doubleValA);
BigDecimal b2 = new BigDecimal(doubleValB);
return a2.subtract(b2).doubleValue();
}
3. 相乘
/**
* 相乘
* @param double1
* @param double2
* @return
*/
public static double mul(String doubleValA, String doubleValB) {
BigDecimal a2 = new BigDecimal(doubleValA);
BigDecimal b2 = new BigDecimal(doubleValB);
return a2.multiply(b2).doubleValue();
}
4. 相除
/**
* 相除
* @param double1
* @param double2
* @param scale 除不尽时指定精度
* @return
*/
public static double div(String doubleValA, String doubleValB, int scale) {
BigDecimal a2 = new BigDecimal(doubleValA);
BigDecimal b2 = new BigDecimal(doubleValB);
return a2.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
5. 主函数调用
public static void main(String[] args) {
String doubleValA = "3.14159267";
String doubleValB = "2.358";
System.out.println("add:" + add(doubleValA, doubleValB));
System.out.println("sub:" + sub(doubleValA, doubleValB));
System.out.println("mul:" + mul(doubleValA, doubleValB));
System.out.println("div:" + div(doubleValA, doubleValB, 8));
}
结果展示如下所示:
add:5.49959267
sub:0.78359267
mul:7.40787551586
div:1.33231241
所以最好的方法是完全抛弃double,用string和java.math.BigDecimal。
java遵照IEEE制定的浮点数表示法来进行float,double运算。这种结构是一种科学计数法,用符号、指数和尾数来表示,底数定为2——即把一个浮点数表示为尾数乘以2的指数次方再添上符号。具体底层如何存储以及如何进行运行请继续关注我的博客,后续我会将详情总结好的。
JAVA浮点数计算精度损失底层原理与解决方案的更多相关文章
- jmeter 中 浮点数计算精度问题
jmeter 中 浮点数计算精度问题解决方法: 编写 beanshell 时使用 java.math.BigDecimal 方法构造,使用 BigDecimal 并且一定要用 String 来够造. ...
- (Jquery)避免数据相加小数点后产生多位数和计算精度损失
/** * 加法运算,避免数据相加小数点后产生多位数和计算精度损失. * * @param num1加数1 | num2加数2 */ function numAdd(num1, num2) { var ...
- 如何避开JavaScript浮点数计算精度问题(如0.1+0.2!==0.3)
不知道大家在使用JS的过程中有没有发现某些浮点数运算的时候,得到的结果存在精度问题:比如0.1 + 0.2 = 0.30000000000000004以及7 * 0.8 = 5.60000000000 ...
- 关于js浮点数计算精度不准确问题的解决办法
今天在计算商品价格的时候再次遇到js浮点数计算出现误差的问题,以前就一直碰到这个问题,都是简单的使用tofixed方法进行处理一下,这对于一个程序员来说是及其不严谨的.因此在网上收集了一些处理浮点数精 ...
- 学以致用:手把手教你撸一个工具库并打包发布,顺便解决JS浮点数计算精度问题
本文讲解的是怎么实现一个工具库并打包发布到npm给大家使用.本文实现的工具是一个分数计算器,大家考虑如下情况: \[ \sqrt{(((\frac{1}{3}+3.5)*\frac{2}{9}-\fr ...
- Java I/O模型及其底层原理
Java I/O是Java基础之一,在面试中也比较常见,在这里我们尝试通过这篇文章阐述Java I/O的基础概念,帮助大家更好的理解Java I/O. 在刚开始学习Java I/O时,我很迷惑,因为网 ...
- JavaScript数字计算精度丢失的问题和解决方案
一.JS数字精度丢失的一些典型问题 1. 两个简单的浮点数相加:0.1 + 0.2 != 0.3 // true,下图是firebug的控制台截图: 看看java的计算结果:是不是让你很不能接受 再来 ...
- js 浮点数计算精度不准确问题
或许很多人都遇到过,js 对小数的加.减.乘.除时经常得到一些奇怪的结果! 比如 :0.1 + 0.2 = 0.3 ? 这么一个简单的计算,当你用js 计算时会发现结果是:0.30000000000 ...
- 详解JAVA字符串类型switch的底层原理
基础 我们现在使用的Java的版本,基本上是都支持String类型的.当然除了String类型,还有int.char.byte.short.enum等等也都是支持的.然而在其底部实现中,还是基于 整型 ...
随机推荐
- python实现端口扫描器/DoS/DDoS
整理github,梳理下Python小工具.以下是python实现的DoS/DDoS/端口扫描器(github). 一.DoS SYN Flood是当前最流行的DoS(拒绝服务攻击)与DdoS(分布式 ...
- Linux - ubuntu中vi不能正常使用方向键与退格键的问题
一度怀疑是键盘坏了! 之前安装solaris也是这个问题! 重新安装vim就可以了! $sudo apt-get remove vim-common $sudo apt-get install vim
- 图表工具--- ECharts.js学习(一) 简单入门
ECharts.js学习(一) 在项目开发的时候,在前端的数据需要用图表的形式展示.网上搜索了一下,发现有几种统计图库.具体有哪几种可以看: 前端开发者常用的9个JavaScript图表库 EChar ...
- ASP.net core 2.0.0 中 asp.net identity 2.0.0 的基本使用(三)—用户账户及cookie配置
修改用户账户及cookie配置 一.修改密码强度和用户邮箱验证规则: 打开Startup.cs,找到public void ConfigureServices(IServiceCollection s ...
- 谈Ajax的Get和Post的区别
Get方式: 用get方式可传送简单数据,但大小一般限制在1KB下,数据追加到url中发送(http的header传送),也就是说,浏览器将各个表单字段元素及其数据按照URL参数的格式附加在请求行 ...
- linux_定时任务
什么是定时任务? linux系统自身定期执行的任务和工作: 轮训系统日志.备份系统数据.清理缓存等 var/log/messages # 系统日志文件, ll /etc/|grep cron # 查询 ...
- Django 中 makemigrations、migrate时 No changes detected
Django创建的项目中,需要更改.增加.删除表中的某些属性,性急直接把之前数据库表删除了,之后再执行: python manage.py makemigrations python manage.p ...
- 如何用Python爬虫实现百度图片自动下载?
Github:https://github.com/nnngu/LearningNotes 制作爬虫的步骤 制作一个爬虫一般分以下几个步骤: 分析需求 分析网页源代码,配合开发者工具 编写正则表达式或 ...
- 为什么.Net平台不支持程序集卸载(Assembly.Unload)?
我们知道在.net平台中反射提供了在运行时动态的获得程序或程序集中每一个类型(包括类.结构.委托.接口和枚举等)的成员和成员的信息,从而使得我们开发人员在运行时能够利用这些信息构造和使用对象.我们知道 ...
- 【原创】源码角度分析Android的消息机制系列(二)——ThreadLocal的工作过程
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 在上一篇文章中,我们已经提到了ThreadLocal,它并非线程,而是在线程中存储数据用的.数据存储以后,只能在指定的线程中获取到数据,对于其 ...