java⼩数丢失精度_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,来组合加出我们要转换的数据值,但显然不是所有的数都能够组合出来的。如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);
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);
// 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;
}bigdecimal除法保留小数
return 0 == b1pareTo(b2);
}
}
标签:Java,运算,double,valueOf,b1,b2,⼩数,BigDecimal

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。