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,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);
}

}

原文地址https://www.cnblogs.com/wunsiang/p/12811661.html

相关文章
|
SQL IDE 算法
《从头开始学java,一天一个知识点》之:运算符与表达式:算术、比较和逻辑运算
**你是否也经历过这些崩溃瞬间?** - 看了三天教程,连`i++`和`++i`的区别都说不清 - 面试时被追问&quot;`a==b`和`equals()`的区别&quot;,大脑突然空白 - 写出的代码总是莫名报NPE,却不知道问题出在哪个运算符 这个系列为你打造Java「速效救心丸」,每天1分钟,地铁通勤、午休间隙即可完成学习。直击高频考点和实际开发中的「坑位」,拒绝冗长概念,每篇都有可运行的代码示例。明日预告:《控制流程:if-else条件语句实战》。
341 6
【Java基础面试十一】、int和Integer有什么区别,二者在做==运算时会得到什么结果?
这篇文章解释了Java中`int`基本数据类型和其包装类`Integer`之间的区别,并指出在进行`==`运算时,`Integer`会拆箱为`int`类型,然后比较它们的值是否相等。
【Java基础面试十一】、int和Integer有什么区别,二者在做==运算时会得到什么结果?
|
Java
Java中将保留四位小数的Double转换为String的方法详解
选择合适的方法,可以使代码更加简洁、高效,同时也能满足不同场景下的需求。
793 5
|
算法 Java
Java数据结构与算法:位运算之与、或、异或运算
Java数据结构与算法:位运算之与、或、异或运算
|
Java
Java的double值保留2位小数
【6月更文挑战第16天】Java的double值保留2位小数
825 0
|
JavaScript Java
Java long传到前台精度损失解决方案
Java long传到前台精度损失解决方案
245 1
|
存储 算法 Java
Java赋值运算
Java赋值运算
265 3
|
存储 Java 数据安全/隐私保护
【Java探索之旅】运算符解密 位运算,移位运算
【Java探索之旅】运算符解密 位运算,移位运算
218 0
|
Java 程序员
【JAVA面试题】基本类型的强制类型转换是否会丢失精度?引用类型的强制类型转换需要注意什么?
【JAVA面试题】基本类型的强制类型转换是否会丢失精度?引用类型的强制类型转换需要注意什么?
|
Java 程序员
Java代码竟能如此轻松表示数学函数运算
Java代码竟能如此轻松表示数学函数运算
220 0