Java中的小数运算与精度损失
float、double类型的问题
我们都知道,计算机是使用二进制存储数据的。而平常生活中,大多数情况下我们都是使用的十进制,因此计算机显示给我们看的内容大多数也是十进制的,这就使得很多时候数据需要在二进制与十进制之间进行转换。对于整数来说,两种进制可以做到一一对应。而对于小数来讲就不是这样的啦。
我们先来看看十进制小数转二进制小数的方法
对小数点以后的数乘以2,会得到一个结果,取结果的整数部分(不是1就是0),然后再用小数部分再乘以2,再取结果的整数部分……以此类推,直到小数部分为0或者位数已经够了。顺序取每次运算得到的整数部分,即为转换后的小数部分。
演示:
0.125 ×2=0.25 .......................0
0.25×2=0.5.............................0
0.5×2=1.0................................1
即 0.125的二进制表示为小数部分为0.001
其实我们可以看出,这种方法实质上就是用1/2,1/4,8/1...来组合加出我们要转换的数据值,但显然不是所有的数都能够组合出来的。如0.1。
0.1×2=0.2 .....................0
0.2×2=0.4 ......................0
0.4×2=0.8 .....................0
0.8×2=1.6.......................1
0.6×2=1.2.......................1
0.2×2=0.4.......................0
.....
从上述计算过程我们可以看出,这是个无限小数,所以在这种情况下我们的float、double只能舍去一些位。
那为什么我们在直接给float赋值在输出时没有看到精度损失而在运算时却会出现呢?
确实是这样,如下
float a = 0.2f;
System.out.println(a);
//输出0.2
对于上述情况我只是查了资料,好像是因为编译器会进行优化,当我们存储的数据特别接近的时候,编译器会很贴心的返回我们想看到的数值(即二进制浮点数并不能准确的表示0.1这个十进制小数,它使用了0.100000001490116119384765625来代替0.1。),至于到了运算中,就会出现精度损失较大从而看到了真相。如果这块说的不对欢迎小伙伴们在评论区指正!
解决方法
BigDecimal 原理
我们一般会使用
BigDecimal 来避免出现精度丢失问题,至于为什么BigDecimal 可以避免,而float或double不行,我们在此不详细讨论,简单来说就是BigDecimal 通过借助整数来表示小数的方式,因为对于整数而言,二进制和十进制是完全一一对应的,用整数来表示小数,再记录下小数的位数,就可以完美的解决该问题。
BigDecimal 用法
java.math.BinInteger 类和 java.math.BigDecimal 类都是Java提供的用于高精度计算的类.其中 BigInteger 类是针对大整数的处理类,而 BigDecimal 类则是针对大小数的处理类.
BigDecimal构造方法
BigDecimal BigDecimal(double d); //不允许使用
BigDecimal BigDecimal(String s); //常用,推荐使用
static BigDecimal valueOf(double d); //常用,推荐使用
- double 参数的构造方法,不允许使用!!!!因为它不能精确的得到相应的值;
- String 构造方法是完全可预知的: 写入 new BigDecimal("0.1") 将创建一个 BigDecimal,它正好等于预期的0.1; 因此,通常建议优先使用 String 构造方法;
- 静态方法 valueOf(double val) 内部实现,仍是将 double 类型转为 String 类型; 这通常是将 double(或float)转化为 BigDecimal 的首选方法;
测试
System.out.println(new BigDecimal(0.1));
System.out.println(BigDecimal.valueOf(0.1));
\\输出*****************************************
0.1000000000000000055511151231257827021181583404541015625
0.1
BigDecimal常用操作
我们通过一个工具类源码来体会BigDecimal的常规用法
package com.util;
import java.math.BigDecimal;
/**
* 提供精确的浮点数运算(包括加、减、乘、除、四舍五入)工具类
*/
public class ArithUtil {
// 除法运算默认精度
private static final int DEF_DIV_SCALE = 10;
private ArithUtil() {
}
/**
* 精确加法
*/
public static double add(double value1, double value2) {
BigDecimal b1 = BigDecimal.valueOf(value1);
BigDecimal b2 = BigDecimal.valueOf(value2);
return b1.add(b2).doubleValue();
}
/**
* 精确减法
*/
public static double sub(double value1, double value2) {
BigDecimal b1 = BigDecimal.valueOf(value1);
BigDecimal b2 = BigDecimal.valueOf(value2);
return b1.subtract(b2).doubleValue();
}
/**
* 精确乘法
*/
public static double mul(double value1, double value2) {
BigDecimal b1 = BigDecimal.valueOf(value1);
BigDecimal b2 = BigDecimal.valueOf(value2);
return b1.multiply(b2).doubleValue();
}
/**
* 精确除法 使用默认精度
*/
public static double div(double value1, double value2) throws IllegalAccessException {
return div(value1, value2, DEF_DIV_SCALE);
}
/**
* 精确除法
* @param scale 精度
*/
public static double div(double value1, double value2, int scale) throws IllegalAccessException {
if(scale < 0) {
throw new IllegalAccessException("精确度不能小于0");
}
BigDecimal b1 = BigDecimal.valueOf(value1);
BigDecimal b2 = BigDecimal.valueOf(value2);
// return b1.divide(b2, scale).doubleValue();
return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 四舍五入
* @param scale 小数点后保留几位
*/
public static double round(double v, int scale) throws IllegalAccessException {
return div(v, 1, scale);
}
/**
* 比较大小
*/
public static boolean equalTo(BigDecimal b1, BigDecimal b2) {
if(b1 == null || b2 == null) {
return false;
}
return 0 == b1.compareTo(b2);
}
}
Java中的小数运算与精度损失的更多相关文章
- JAVA中的小数
JAVA中的小数称为浮点数 1.有两种类型: float:单精度浮点数.4个字节. double:双精度浮点数.8个字节. 2.类型转换 容量小 -------------------------- ...
- Java中的递归运算
Java中的递归运算是一种在自己的方法内部调用自己的方法 递归的设计思想是:把一个复杂的问题,分解为若干个等同的子问题,重复执行,直到之问题能够简单到直接求解,这样复杂的问题就得以解决. 递归运算有两 ...
- Java中取小数点后两位(四种方法)
摘自http://irobot.iteye.com/blog/285537 Java中取小数点后两位(四种方法) 一 Long是长整型,怎么有小数,是double吧 java.text.D ...
- java 常用类库:BigInteger大整数;BigDecimal大小数(解决double精度损失);
大整数BigInteger package com.zmd.common_class_libraries; import java.math.BigInteger; /** * @ClassName ...
- 关于Java中用Double型运算时精度丢失的问题
注:转自 https://blog.csdn.net/bleach_kids/article/details/49129943 在使用Java,double 进行运算时,经常出现精度丢失的问题,总是在 ...
- Java中double类型数据的精度问题
今天在写段代码模拟计算器的时候,偶然发现,当我进行小数运算的时候,竟然出现了令我惊讶的结果,后来问了问度娘,才晓得,原来这里面还有点知识呢,下面是介绍: 你猜下面几句的结果是多少? public cl ...
- 妥协与取舍,解构C#中的小数运算
题外话 正文开始之前,我首先要感谢博客园提供的这个优秀的平台.通过在这个优秀的平台上和很多志同道合的朋友交流,互相帮助,我也很荣幸的获得了15年的微软MVP的奖项.也使我更加坚信了代码改变世界.感激! ...
- JAVA中double类型运算结果异常的解决
问题: 对两个double类型的值进行运算,有时会出现结果值异常的问题.比如: System.out.println(19.99+20); System.out.println(1.0-0.66); ...
- 精确计算java中float和double的精度
[本文相关的代码放在github上.地址为:https://github.com/VigourJiang/StructuredFloat] Java中double类型的格式基本遵循IEEE 754标准 ...
随机推荐
- springBoot集成zuul路由forward,设置setSendZuulResponse无效
正确书写方式如下: RequestContext ctx = RequestContext.getCurrentContext(); ctx.setSendZuulResponse(false); c ...
- 运输问题中产销不平衡问题(表上作业法和LINGO方法)
对于产销不平衡问题有两种情况: 供大于求(产大于销)→增加虚拟销地 供不应求(产小于销)→增加虚拟产地 例如以下例题: 这个题中,总产量为55,总销量为60,故而我们知道这个问题属于供不应求. 1.这 ...
- Pytest系列(6) - conftest.py的详细讲解
如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html 什么是conftest.py 可以 ...
- Docker基础修炼1--Docker简介及快速入门体验
本文作为Docker基础系列第一篇文章,将详细阐述和分析三个问题:Docker是什么?为什么要用Docker?如何快速掌握Docker技术? 本系列文章中Docker的用法演示是基于CentOS7进行 ...
- 电商平台--Mysql主从搭建(2)
Master上授权从库: ```grant replication slave on *.* to slave1@ip identified by 'password';``` 基于数据库hotcop ...
- BadMethodCallException : Call to undefined method App\Models\Article::setContainer()
如果你执行 php artisan db:seed 发生如下错误 说是模型中不存在 静态方法 setContainer()方法,那么你应该检查下你的DatabaseSeeder.php 文件 中的 r ...
- Centos7如何安装MySQL
参考博文: 1.安装 https://blog.csdn.net/qq_36582604/article/details/80526287 2.改密码 https://blog.csdn.net/we ...
- javascript入门 之 ztree(二 标准json数据)
1.代码 <!DOCTYPE html> <HTML> <HEAD> <TITLE> ZTREE DEMO - Standard Data </T ...
- 最全的中文NLP资源库,你确定不来看一下吗?
最全的中文NLP资源库,你确定不来看一下吗? 22/100 发布文章 qq_39248703 hello,小伙伴们大家好,今天给大家分享NLP资源库,可以说是最全的资源库了,很多包非常有趣,值得收藏, ...
- 泛型方法或泛型类中的方法是内部调用、PInvoke 或是在 COM 导入类中定义的。
泛型基类中引用Api函数定义时static extern,在子类中会提示: 未处理TypeLoadException 泛型方法或泛型类中的方法是内部调用.PInvoke 或是在 COM 导入类中定义的 ...