浅析Java BigDecimal为什么可以不丢失精度

 更新时间:2024年03月22日 14:31:55   作者:CaryDai  
在金融领域,为了保证数据的精度,往往会使用BigDecimal,所以这篇文章主要来和大家探讨下为什么BigDecimal可以保证精度不丢失,感兴趣的可以了解下

在金融领域,为了保证数据的精度,往往会使用BigDecimal。本文就来探讨下为什么BigDecimal可以保证精度不丢失。

类介绍

首先来看一下BigDecimal的类声明以及几个属性:

public class BigDecimal extends Number implements Comparable<BigDecimal> {
    // 该BigDecimal的未缩放值
    private final BigInteger intVal;
    // 精度,可以理解成小数点后的位数
    private final int scale;
    // BigDecimal中的十进制位数,如果位数未知,则为0(备用信息)
    private transient int precision;
    // Used to store the canonical string representation, if computed.
    // 这个我理解就是存实际的BigDecimal值
    private transient String stringCache;
    // 扩大成long型数值后的值
    private final transient long intCompact;
}

从例子入手

通过debug来发现源码中的奥秘是了解类运行机制很好的方式。 请看下面的testBigDecimal方法:

@Test
public void testBigDecimal() {
    BigDecimal bigDecimal1 = BigDecimal.valueOf(2.36);
    BigDecimal bigDecimal2 = BigDecimal.valueOf(3.5);
    BigDecimal resDecimal = bigDecimal1.add(bigDecimal2);
    System.out.println(resDecimal);
}

在执行了BigDecimal.valueOf(2.36)后,查看debug信息可以发现上述提到的几个属性被赋了值:

接下来进到add方法里面,看看它是怎么计算的:

/**
 * Returns a BigDecimal whose value is (this + augend), 
 * and whose scale is max(this.scale(), augend.scale()).
 */
public BigDecimal add(BigDecimal augend) {
    if (this.intCompact != INFLATED) {
        if ((augend.intCompact != INFLATED)) {
            return add(this.intCompact, this.scale, augend.intCompact, augend.scale);
        } else {
            return add(this.intCompact, this.scale, augend.intVal, augend.scale);
        }
    } else {
        if ((augend.intCompact != INFLATED)) {
            return add(augend.intCompact, augend.scale, this.intVal, this.scale);
        } else {
            return add(this.intVal, this.scale, augend.intVal, augend.scale);
        }
    }
}

看一下传进来的值:

进入第8行的add方法:

private static BigDecimal add(final long xs, int scale1, final long ys, int scale2) {
    long sdiff = (long) scale1 - scale2;
    if (sdiff == 0) {
        return add(xs, ys, scale1);
    } else if (sdiff < 0) {
        int raise = checkScale(xs,-sdiff);
        long scaledX = longMultiplyPowerTen(xs, raise);
        if (scaledX != INFLATED) {
            return add(scaledX, ys, scale2);
        } else {
            BigInteger bigsum = bigMultiplyPowerTen(xs,raise).add(ys);
            return ((xs^ys)>=0) ? // same sign test
                new BigDecimal(bigsum, INFLATED, scale2, 0)
                : valueOf(bigsum, scale2, 0);
        }
    } else {
        int raise = checkScale(ys,sdiff);
        long scaledY = longMultiplyPowerTen(ys, raise);
        if (scaledY != INFLATED) {
            return add(xs, scaledY, scale1);
        } else {
            BigInteger bigsum = bigMultiplyPowerTen(ys,raise).add(xs);
            return ((xs^ys)>=0) ?
                new BigDecimal(bigsum, INFLATED, scale1, 0)
                : valueOf(bigsum, scale1, 0);
        }
    }
}

这个例子中,该方法传入的参数分别是:xs=236,scale1=2,ys=35,scale2=1 该方法首先计算scale1 - scale2,根据差值走不同的计算逻辑,这里求出来是1,所以进入到最下面的else代码块(这块是关键):

  • 首先17行校验了一下数值范围
  • 18行将ys扩大了10的n次倍,这里n=raise=1,所以返回的scaledY=350
  • 接着就进入到20行的add方法:
private static BigDecimal add(long xs, long ys, int scale){
    long sum = add(xs, ys);
    if (sum!=INFLATED)
        return BigDecimal.valueOf(sum, scale);
    return new BigDecimal(BigInteger.valueOf(xs).add(ys), scale);
}

这个方法很简单,就是计算和,然后返回BigDecimal对象:

结论

所以可以得出结论:BigDecimal在计算时,实际会把数值扩大10的n次倍,变成一个long型整数进行计算,整数计算时自然可以实现精度不丢失。同时结合精度scale,实现最终结果的计算。

到此这篇关于浅析Java BigDecimal为什么可以不丢失精度的文章就介绍到这了,更多相关Java BigDecimal精度丢失内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java实现自动生成缩略图片

    Java实现自动生成缩略图片

    这篇文章主要为大家详细介绍了Java实现自动生成缩略图片,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • Java实现多级表头和复杂表头的导出功能

    Java实现多级表头和复杂表头的导出功能

    这篇文章主要为大家详细介绍了Java实现多级表头和复杂表头的导出功能的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-03-03
  • 详细介绍Java后端API接口开发规范

    详细介绍Java后端API接口开发规范

    在Java后端开发中,API接口的设计与开发至关重要,本文详细介绍了命名规范、接收参数规范、参数检验、接收方式规范、异常类处理、统一返回格式以及幂等性等方面的开发规范,提供了实际代码示例,需要的朋友可以参考下
    2024-10-10
  • 浅析Spring的JdbcTemplate方法

    浅析Spring的JdbcTemplate方法

    本篇浅析Spring的JdbcTemplate方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-01-01
  • Linux下java环境配置图文方法

    Linux下java环境配置图文方法

    这篇文章主要介绍了Linux下java环境配置图文方法,需要的朋友可以参考下
    2023-06-06
  • RocketMQMessageListener注解对rocketmq消息的消费实现机制

    RocketMQMessageListener注解对rocketmq消息的消费实现机制

    这篇文章主要为大家介绍了RocketMQMessageListener注解对rocketmq消息的消费实现机制源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • java单例模式使用详解

    java单例模式使用详解

    这篇文章主要介绍了java单例模式使用方法,通常它的实现方式分为懒汉式实现和饿汉式,下面是详细说明加示例,需要的朋友可以参考下
    2014-04-04
  • Java之SpringCloud nocos注册中心讲解

    Java之SpringCloud nocos注册中心讲解

    这篇文章主要介绍了Java之SpringCloud nocos注册中心讲解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • Java的线程与进程以及线程的四种创建方式

    Java的线程与进程以及线程的四种创建方式

    这篇文章主要为大家详细介绍了Java的线程与进程以及线程的四种创建方式,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • 用Java实现希尔排序的示例

    用Java实现希尔排序的示例

    问题:现有一段程序S,可以对任意n个数进行排序。如果现在需要对n^2个数进行排序,最少需要调用S多少次?只允许调用S,不可以做别的操作。我们用希尔排序来做解决这个
    2013-11-11

最新评论