【BigDecimal(double)和 BigDecimal(String)的区别】

发布时间:2023年12月21日

?典型解析

两者有区别,而且区别很大。


因为double是不精确的,所以使用一个不精确的数字来创建BigDeciaml,得到的数字也是不精确的。如0.1这个数字,double只能表示他的近似值。


所以,当我们使用new BigDecimal(0.1)创建一个BigDecimal 的时候,其实创建出来的值并不是正好等于0.1的。


而是0.1000000000000000055511151231257827021181583404541015625这是因为double自身表示的只是一个近似值。


而对于BiaDecima(String),当我们使用new BigDecimal("0.1”)创建一BigDecimal 的时候,其实创建出来的值正好就是等于0.1的。


那么他的标度也就是1 。

?拓展知识仓


在《阿里巴巴Java开发手册》中有一条建议,或者说是要求:

在这里插入图片描述

?BigDecimal 如何精确计数


如果大家看过BigDecimal的源码,其实可以发现,实际上一个BigDecimal是通过一个 "无标度值” 和一个 "标度” 来表示一个数的。


在BigDecimal中,标度是通过scale字段来表示的。


而无标度值的表示比较复杂。当unscaled value超过阈值(默认为Long.MAX _VALUE)时采用intVal字段存储unscaled value, intCompact字段存诸Long.MIN_VALUE,否则对 unscaled value行压缩存储到ong型的intCompact字段用于后续计算,intVal为空。


涉及到的字段就是这几个:


public class BigDecimal extends Number implements Comparable <BigDecimal>  {
	private final BigInteger intVal;
	private final int scale;
	private final transient long intCompact;
}

关于无标度值的压缩机制大家了解即可,不是本文的重点,大家只需要知道BigDecimal主要是通过一个无标度值和标度来表示的就行了。


?那么标度到底是什么呢?


除了scale这个字段,在BigDecimal中还提供了scale()方法,用来返回这个BigDecimal的标度。


/**
 * Returns the <i>scale</i> of this f@code BigDecimal}. If zero
 * or positive, the scale is the number of digits to the right of
 * the decimal point. If negative, the unscaled value of the
 *  number is multiplied by ten to the power of the negation of the
 * scale. For example, a scale of (@code -31 means the unscaled
 *  value is multiplied by 1000.
 *
 *
 *@return the scale of this {@code BigDecimal}.
 */

public int scale()  {
	return scale;
}

那么,scale到底表示的是什么,其实上面的注释已经说的很清楚了:

如果scale为零或正值,则该值表示这个数字小数点右侧的位数。如果scale为负数,则该数字的无标度值需要乘以10的该负数的绝对值的幂。例如,scale为-3,则这个数需要乘1000,即在末尾有3个0。


如123.123,那么如果使用BigDecimal表示,那么他的无标度值为123123,他的标度为3。


而二进制无法表示的0.1,使用BigDecimal就可以表示了,及通过无标度值1和标度1来表示。


我们都知道,想要创建一个对象,需要使用该类的构造方法,在BigDecimal中一共有以下4个构造方法。(上一篇博文有说过: ?为什么不能用BigDecimal的equals方法做等值比较?)


BigDecimal(int)
BigDecimal(double)
BigDecimal(long)
BigDecimal(string)


以上四个方法,创建出来的的BigDecimal的标度 (scale) 是不同的。


其中 BigDecimal(int)和BigDecimal(long) 比较简单,因为都是整数,所以他们的标度都是0。


而 BigDecimal(double) 和 BigDecimal(String) 的标度就有很多学问了。


?BigDecimal(double) 有什么问题


BigDecimal中提供了一个通过double创建BigDecimal的方法 -BigDecima(double),但是,同时也给我们留了一个坑!


因为我们知道,double表示的小数是不精确的,如0.1这个数字,double只能表示他的近似值。


所以,当我们使用new BigDecimal(0.1)创建一个BigDecimal 的时候,其实创建出来的值并不是正好等于0.1的。


而是0.1000000000000000055511151231257827021181583404541015625这是因为double自身表示的只是一个近似值。


在这里插入图片描述

所以,如果我们在代码中,使用BigDecimal(double) 来创建一个BigDecimal的话,那么是损失了精度的,这是极其严重的。


?使用BigDecimal(String)创建


那么,该如何创建一个精确的BigDecimal来表示小数呢,答案是使用String创建。

而对于BigDecimal(String),当我们使用new BigDecimal(“0.1”)创建一个BigDecimal 的时候,其实创建出来的值正好就是等于0.1的。


那么他的标度也就是1。


但是需要注意的是,new BigDeimal(“0.10000”) 和new BigDecimal (“0.1”) 这两个数的标度分别是5和1,如果使用BigDecimal的equals方法比较,得到的结果是false。


那么,想要创建一个能精确的表示0.1的BigDecimal,请使用以下两种方式:


BigDecimal recommend1 = new BigDecimal("0.1");
BigDecimal recommend2 = BigDecimal.value0f(0.1);

这里,留一个思考题,BigDecimal.valueOf是调用Double.toString方法实现的,那么,既然double都是不精确的,BigDecimal.valueOf(0.1)怎么保证精确呢?


? 总结


因为计算机采用二进制处理数据,但是很多小数,如0.1的二进制是一个无限循环小数,而这种数字在计算机中是无法精确表示的。


所以,人们采用了一种通过近似值的方式在计算机中表示,于是就有了单精度浮点数和双精度浮点数等。


所以,作为单精度浮点数的float和双精度浮点数的double,在表示小数的时候只是近似值,并不是真实值。


所以,当使用BigDecimal(Double)创建一个的时候,得到的BigDecimal是损失了精度的。


而使用一个损失了精度的数字进行计算,得到的结果也是不精确的。


想要避免这个问题,可以通过BigDeimal(String)的方式创建BigDecimal,这样的情况下,0.1就会被精确的表示出来。


其表现形式是一个无标度数值1,和一个标度1的组合。

?博文中思考题答案

BigDecimal.valueOf方法并不直接调用Double.toString方法。实际上,BigDecimal.valueOf方法接受一个double类型的参数,并将其转换为BigDecimal对象。

在Java中,double类型是一个浮点数类型,它具有有限的精度。这意味着在某些情况下,使用double类型表示某些数值可能会产生精度损失或舍入误差。

BigDecimal类型是Java提供的一个精确的十进制数值类型。它具有更高的精度和更大的范围,可以准确地表示和计算任意精度的十进制数。

当你使用BigDecimal.valueOf(0.1)时,实际上是将double类型的值0.1转换为BigDecimal对象。由于BigDecimal类型具有更高的精度,它可以准确地表示0.1,而不会产生精度损失或舍入误差。

需要注意的是,虽然BigDecimal类型可以提供更高的精度,但它也可能比使用double类型更加耗时和占用更多的内存。因此,在需要高精度计算的情况下,使用BigDecimal类型是合适的,但在不需要高精度的情况下,使用double类型可能更加高效。


💡这里我们编写一个Demo,使用 BigDecimal 进行高精度计算:


import java.math.BigDecimal;  
import java.math.RoundingMode;  
  
public class BigDecimalCalculator {  
    public static void main(String[] args) {  
        BigDecimal a = new BigDecimal("10.5");  
        BigDecimal b = new BigDecimal("2.5");  
        BigDecimal c = new BigDecimal("3.2");  
        BigDecimal d = new BigDecimal("4.8");  
  
        // 加法  
        BigDecimal sum = a.add(b);  
        System.out.println("加法结果:" + sum);  
  
        // 减法  
        BigDecimal diff = a.subtract(b);  
        System.out.println("减法结果:" + diff);  
  
        // 乘法  
        BigDecimal product = a.multiply(b);  
        System.out.println("乘法结果:" + product);  
  
        // 除法  
        BigDecimal quotient = a.divide(b, 2, RoundingMode.HALF_UP);  
        System.out.println("除法结果:" + quotient);  
  
        // 幂运算  
        BigDecimal power = a.pow(2);  
        System.out.println("幂运算结果:" + power);  
  
        // 自定义函数:计算平方根  
        BigDecimal sqrt = a.sqrt();  
        System.out.println("平方根结果:" + sqrt);  
  
        // 自定义函数:计算三角函数值  
        BigDecimal sin = a.sin();  
        System.out.println("正弦值结果:" + sin);  
    }  
}
文章来源:https://blog.csdn.net/Java_Yangxiaoyuan/article/details/135140217
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。