【Java】BigDecimal类型——BigDecimal 为什么可以保证精度不丢失

目录

  • 简介
  • 类介绍
  • 案例分析
  • 总结
  • BigDecimal类型的使用场景
  • MySQL中存储BigDecimal类型数据
  • 补充:BigDecimal类型使用时的注意事项
  • BigDecimal类型的其他使用

简介

  • BigDecimal是Java中的一个类,用于处理大数运算。它提供了精确的数值计算,可以处理任意位数的整数和小数,避免了使用浮点数时可能出现的精度问题。

  • 在普通的数值计算中,使用Java的基本数据类型(如int、double)进行运算时,有时会出现精度丢失的情况。例如,计算0.1 + 0.2时,使用double类型会得到0.30000000000000004,而不是预期的0.3。

  • BigDecimal类提供了一系列的构造方法,可以将基本数据类型、字符串等转换为BigDecimal对象。通过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;
}

案例分析

@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方法里面,看看它是怎么计算的

    // Arithmetic Operations
    /**
     * Returns a {@code BigDecimal} whose value is {@code (this +
     * augend)}, and whose scale is {@code max(this.scale(),
     * augend.scale())}.
     *
     * @param  augend value to be added to this {@code BigDecimal}.
     * @return {@code this + augend}
     */
    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);
            }
        }
    }

结合传入的值来看
在这里插入图片描述
进入这个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行校验了一下数值范围:int raise = checkScale(ys,sdiff);
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,实现最终结果的计算。

BigDecimal类型的使用场景

BigDecimal类型主要用于精确计算,特别是在需要处理大数或需要保留小数位数精确的场景中。以下是一些使用BigDecimal类型的常见场景:

  1. 财务计算:在金融或财务领域,需要对金额、利率、汇率等进行精确计算和舍入,避免由于浮点数运算带来的精度问题。

  2. 税务计算:对于需要计算税额、税率等的场景,BigDecimal类型可以确保计算结果的准确性,并避免误差。

  3. 计量单位转换:在需要进行单位转换的场景中,BigDecimal可以提供精确的计算结果,确保转换的准确性。

  4. 程序性能测试:在进行性能测试时,可能需要进行大量的计算操作,使用BigDecimal类型可以避免浮点数运算带来的精度误差,确保测试结果的准确性。

  5. 商业应用开发:在开发商业应用时,可能需要处理复杂的数值计算,如利润率、销售额等,使用BigDecimal类型可以确保计算结果的准确性,避免精度丢失。

总之,如果需要进行精确计算、保留小数位数或处理大数时,在上述场景中使用BigDecimal类型是一个很好的选择。

MySQL中存储BigDecimal类型数据

  • 在MySQL中,可以使用DECIMAL数据类型来存储BigDecimal类型的数据。DECIMAL类型用于存储精确的数值,支持指定精度和小数位数。

  • DECIMAL的语法:DECIMAL(M, D)

    • M表示总的位数,
    • D表示小数点后的位数。
  • 例如,要存储一个精确到小数点后两位的数字,可以使用DECIMAL(6,2)类型。可以在创建表时指定DECIMAL类型的字段,例如:

    CREATE TABLE example (
      id INT PRIMARY KEY,
      amount DECIMAL(10,2)
    );
    
  • 在上述例子中,amount字段的类型为DECIMAL,总共有10位,其中小数部分占2位。

  • 可以通过INSERT语句插入BigDecimal值到DECIMAL字段中,例如:

    INSERT INTO example (id, amount) VALUES (1, 1234.56);
    
  • 可以通过SELECT语句查询DECIMAL字段的值,例如:

    SELECT amount FROM example WHERE id = 1;
    

注意,需要根据实际情况来确定DECIMAL字段的合适的精度和小数位数,以确保存储和计算的准确性。

补充:BigDecimal类型使用时的注意事项

在使用BigDecimal类型时,有一些注意事项需要注意:

  1. 避免使用浮点数构造BigDecimal:浮点数在计算机中以二进制表示,可能会导致精度丢失。建议使用字符串或整数构造BigDecimal对象,以确保精确性。

  2. 使用BigDecimal的字符串构造函数:使用BigDecimal的字符串构造函数可以确保精确性,例如:new BigDecimal(“0.1”)。而使用new BigDecimal(0.1)可能会导致精度丢失。

  3. 设置精度和舍入模式:可以使用setScale()方法设置BigDecimal的精度和舍入模式。精度指的是小数部分的位数,舍入模式指的是如何处理小数位数超过精度的情况。

  4. 避免使用equals()方法比较BigDecimal:由于BigDecimal是一个引用类型,使用equals()方法比较BigDecimal对象时,需要确保比较的精确度。推荐使用compareTo()方法来进行比较。

  5. 避免使用BigDecimal进行大量的运算:由于BigDecimal对象是不可变的,每次进行运算都会创建一个新的BigDecimal对象。如果需要进行大量的计算,可以考虑使用BigDecimal的可变版本MutableBigDecimal。

  6. 注意处理除法运算的精度:在进行除法运算时,需要注意处理小数位数的精度和舍入模式。可以使用divide()方法指定精度和舍入模式。

总之,使用BigDecimal时需要注意精度、舍入模式和数值的构造方式,以确保计算的准确性和一致性。同时,了解和使用BigDecimal的各种方法,可以更好地处理数值计算和比较。

BigDecimal类型的其他使用

除了上述注意事项,以下是一些BigDecimal类型的其他使用方法:

  1. 加法和减法:可以使用add()方法进行两个BigDecimal对象的相加,使用subtract()方法进行相减。例如:

       BigDecimal num1 = new BigDecimal("10.5");
       BigDecimal num2 = new BigDecimal("5.3");
       BigDecimal sum = num1.add(num2);  // 结果为15.8
       BigDecimal difference = num1.subtract(num2);  // 结果为5.2
    
  2. 乘法和除法:可以使用multiply()方法进行乘法运算,使用divide()方法进行除法运算。例如:

       BigDecimal num1 = new BigDecimal("10.5");
       BigDecimal num2 = new BigDecimal("2.5");
       BigDecimal product = num1.multiply(num2);  // 结果为26.25
       BigDecimal quotient = num1.divide(num2);  // 结果为4.2
    
    
  3. 取反操作:可以使用negate()方法对BigDecimal对象进行取反操作。例如:

       BigDecimal num = new BigDecimal("10.5");
       BigDecimal neg = num.negate();  // 结果为-10.5
    
  4. 取绝对值:可以使用abs()方法获取BigDecimal对象的绝对值。例如:

       BigDecimal num = new BigDecimal("-10.5");
       BigDecimal abs = num.abs();  // 结果为10.5
    
  5. 比较大小:可以使用compareTo()方法对两个BigDecimal对象进行大小比较。例如:

       BigDecimal num1 = new BigDecimal("10.5");
       BigDecimal num2 = new BigDecimal("5.3");
       int result = num1.compareTo(num2);  // 结果为1(num1大于num2)
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/726131.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

PCB相关

PCB过孔过流能力计算软件&#xff1a; PCB过孔载流计算器EDA在线工具PCB联盟网 - Powered by Discuz! 孔径&#xff1a;过孔直径 温升&#xff1a;过孔温升标准 参考资料&#xff1a; PCB及钢网与嘉力创标准_嘉立创不支持盲埋孔-CSDN博客&#xff08;待学习&#xff09; PC…

openEuler系统中LVM逻辑卷的创建及扩容与缩容

1、背景说明 安装好openEuler操作系统后为其增加新的磁盘进行逻辑卷的扩容与缩容 本次测试使用VMware Workstation Pro虚拟机增加一个磁盘大小为500GB&#xff0c;虚机不关机直接加盘后&#xff0c;使用ls /dev/sd* 或者fdisk -l 发现没有新加的磁盘设备Disk /dev/sdb &#…

MAVEN-SNAPSHOT和RELEASE + 打包到远程仓库

一、快照版本SNAPSHOT和发布版本RELEASE区别 快照版本SNAPSHOT和发布版本RELEASE区别-CSDN博客 在使⽤maven过程中&#xff0c;我们在开发阶段经常性的会有很多公共库处于不稳定状态&#xff0c;随时需要修改并发布&#xff0c;可能⼀天就要发布⼀次&#xff0c;遇到bug时&am…

[面试题]Kafka

[面试题]Java【基础】[面试题]Java【虚拟机】[面试题]Java【并发】[面试题]Java【集合】[面试题]MySQL[面试题]Maven[面试题]Spring Boot[面试题]Spring Cloud[面试题]Spring MVC[面试题]Spring[面试题]MyBatis[面试题]Nginx[面试题]缓存[面试题]Redis[面试题]消息队列[面试题]…

git merge(3个模式) 与 git rebase 图文详解区别

目录 1 git merge1.1 模式一&#xff1a;fast-forward(–ff)1.2 模式二&#xff1a;non-Fast-forward(–no-ff)1.3 模式三&#xff1a;fast-forward only(–ff-only) 2 git rebase3 区别 1 git merge git merge有好几种不同的模式 默认情况下你直接使用 git merge 命令&#x…

怎么还有人不清楚H3C认证考试流程?给你整理得明明白白

最近不少人都在后台问H3CIE资格认证考试&#xff08;全称&#xff1a;H3C认证路由交换互联网络专家&#xff09;&#xff0c;今天咱们就来聊一聊这个考试流程。第一次报考H3CIE-RS的考生需先参加新华三人才研学中心认可的H3CIE-RS认证培训。 H3C授权培训中心可在新华三官网查询…

迅为RK3568驱动教程第十八期-PWM

系统性PWM课程&#xff0c;完全掌握PWM。采用框架学习法&#xff0c;从基础知识、PWM子系统框架、API函数理论由面到点&#xff0c;逐个击破。通过SG90舵机&#xff0c;呼吸灯的控制把理论转为动手能力。最后从零实现输入捕获驱动程序&#xff0c;深入探究&#xff0c;体验一把…

Python爬虫小白入门(四)PhatomJS+Selenium篇下

一、前言 前文介绍了PhatomJS 和Selenium 的用法&#xff0c;工具准备完毕&#xff0c;我们来看看如何使用它们来改造我们之前写的小爬虫。 我们的目的是模拟页面下拉到底部&#xff0c;然后页面会刷出新的内容&#xff0c;每次会加载10张新图片。 大体思路是&#xff0c;用S…

list的特性及使用

1、list的介绍 1.list是序列容器&#xff0c;允许在序列的任何位置进行时间复杂度为o(1)的插入和删除操作&#xff0c;并且由双向迭代器。 2.list的底层是双链表&#xff0c;双链表不是物理上连续的储存空间&#xff0c;而是不同的地址空间通过next和prev指针连接成顺序表。 …

【每天学会一个渗透测试工具】AppScan安装及使用指南

&#x1f31d;博客主页&#xff1a;泥菩萨 &#x1f496;专栏&#xff1a;Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 https://www.hcl-software.com/appscan AppScan是一种综合型漏洞扫描工具&#xff0c;采用SaaS解决方案&#xff0c;它将所以…

【Tello无人机】使用Matlab完成控制器的设计—建模

模型辨识篇 在实际的无人机系统中&#xff0c;控制器的设计至关重要&#xff0c;它直接影响无人机的稳定性和响应能力。然而&#xff0c;要设计出高效、可靠的控制器&#xff0c;首先必须准确理解无人机的动态行为&#xff0c;这就需要通过收集输入输出数据来辨识其运动学模型。…

天池人脸识别项目复现

1 项目背景 #c 概述 项目的目的 图像分类是整个计算机视觉领域中最基础的任务&#xff0c;也是最重要的任务之⼀&#xff0c;最适合拿来进⾏学习实践。为了让新⼿们能够⼀次性体验⼀个⼯业级别的图像分类任务的完整流程&#xff0c;本次我们选择带领⼤家完成⼀个对图片中⼈脸进…

从0开始C++(二):类、对象、封装

目录 类&对象的概念 类的内容 对象的创建 ● 栈内存对象 ● 堆内存对象 封装 类&对象的概念 类和对象是一个比较抽象的概念&#xff0c;这里直接用一个实例方便理解。 类&#xff1a;类是一个抽象的概念&#xff0c;用来描述同一类对象的特点&#xff08;比如&am…

从零开始如何学习人工智能?

说说我自己的情况&#xff1a;我接触AI的时候&#xff0c;是在研一。那个时候AlphaGo战胜围棋世界冠军李世石是大新闻&#xff0c;人工智能第一次出现我面前&#xff0c;当时就想搞清楚背后的原理以及这些技术有什么作用。 就开始找资料&#xff0c;看视频。随着了解的深入&am…

第 三 方 组 件 e l e m e n t - u i[Vue]

一、组件之间的传值 组件可以由内部的Data提供数据&#xff0c;也可以由父组件通过prop的方式传值。 兄弟组件之间可以通过Vuex等统一数据源提供数据共享 第一种 Movie.vue <template><div><h1>我才不要和你做朋友</h1></div></template&…

怎么为自己的VPS选择合适的CPU和RAM?

为网站选择VPS&#xff08;虚拟专用服务器&#xff09;与为家庭或办公室选择台式机或笔记本电脑没有什么不同。基本上&#xff0c;您要做的就是查看硬件配置并比较功能和价格。 然而&#xff0c;虽然您可能对个人计算机所需的资源类型有一个粗略的估计&#xff0c;但为服务器获…

API接口测试要注意什么?API接口如何开发?

API接口怎么保证数据安全&#xff1f;如何安全调用邮件API接口&#xff1f; API接口不仅能够确保系统的稳定性和可靠性&#xff0c;还能提高开发效率和用户满意度。然而&#xff0c;要进行有效的API接口测试&#xff0c;需要注意多个方面。AokSend将介绍一些关键点&#xff0c…

全面赋能,永久免费!讯飞星火API能力正式免费开放

2023年5月&#xff0c;讯飞星火正式发布&#xff0c;迅速成为千万用户获取知识、学习知识的“超级助手”&#xff0c;成为解放生产力、释放想象力的“超级杠杆”。 2024年5月&#xff0c;讯飞星火API能力正式免费开放&#xff0c;携手生态开发者加快大模型赋能刚需场景。 领…

【软件工程】【22.10】p1

关键字&#xff1a; 软件需求分类、性能需求、数据流图加工、用例关系、捕获系统功能用例、RUP设计层术语、故障、调试、集成测试、需求分析过程、CMMI实践部件、CMMI能力等级 软件需求规约基本性质、模块控制、协作、UML结点、事件、RUP体系结构、RUP移交、静态评估技术、测…

Java共享台球室无人系统支持微信小程序+微信公众号

共享台球室无人系统 &#x1f3b1; 创新台球体验 近年来&#xff0c;共享经济如火如荼&#xff0c;从共享单车到共享汽车&#xff0c;无一不改变着我们的生活方式。而如今&#xff0c;这一模式已经渗透到了更多领域&#xff0c;共享台球室便是其中之一。不同于传统的台球室&a…