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

news2025/2/26 7:28:31

目录

  • 简介
  • 类介绍
  • 案例分析
  • 总结
  • 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)
    

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

51单片机STC89C52RC——2.3 两个独立按键模拟控制LED流水灯方向

目的 按下K1键LED流水向左移动 按下K2键LED流水向右移动 一&#xff0c;STC单片机模块 二&#xff0c;独立按键 2.1 独立按键位置 2.2 独立按键电路图 这里要注意一个设计的bug P3_1 引脚对应是K1 P3_0 引脚对应是K2 要实现按一下点亮、再按一下熄灭&#xff0c;我们就需…

『Z-Weekly Feed 08』加密资产观 | FHE应用前景 | OPAL协议

一位机构投资者的加密资产观 作者&#xff1a;Hongbo 01 &#x1f4a1;TL;DR 在加密投资领域如何找到真正的“价值”&#xff1a;Crypto 作为一种新兴资产&#xff0c;应该找到一种区别于传统公司股票资产的估值方法&#xff0c;本文重点阐述了加密货币作为新的资产类型与传统资…

北京健康检测小程序开发,爱自己,从体检开始

随着经济与科技的不断发展&#xff0c;人们也越来越重视自身的身体健康。面对市面上众多的体检机构与五花八门的体检产品&#xff0c;人们往往没有充足的时间进行筛选&#xff0c;好不容易选好了医院、但进行单项体检的价格较高&#xff0c;选择套餐&#xff0c;又包含了一些不…

Codeforces Round 953 (Div.2)

传送门 A. Alice and Books 时间限制&#xff1a;1秒 空间限制&#xff1a;256MB 输入&#xff1a;标准输入 输出&#xff1a;标准输出 题目描述 Alice 有 n 本书。第 1 本书有 页&#xff0c;第 2 本书有 页&#xff0c;依此类推&#xff0c;第n本…

c库函数:strrchr使用demo案例

1. strrchr库函数说明 头文件 <string.h> 函数形式 char *strrchr( const char *str, int ch ); 功能 在str所指向的空终止字节串中寻找字符ch的最后出现。 参数 str - 指向要分析的空终止字节字符串的指针 ch - 要搜索的字符 返回值 指向 str 中找到的字符的…

【Docker安装】Ubuntu系统下部署Docker环境

【Docker安装】Ubuntu系统下部署Docker环境 前言一、本次实践介绍1.1 本次实践规划1.2 本次实践简介二、检查本地环境2.1 检查操作系统版本2.2 检查内核版本2.3 更新软件源三、卸载Docker四、部署Docker环境4.1 安装Docker4.2 检查Docker版本4.3 配置Docker镜像加速4.4 启动Doc…

关于车规级功率器件热可靠性测试的分享

随着中国电动汽车市场的稳步快速发展和各大车企布局新能源的扩散&#xff0c;推动了车规级功率器件的快速增长。新能源汽车行业和消费电子都会用到半导体芯片&#xff0c;但车规级芯片对外部环境要求很高&#xff0c;涉及到的一致性和可靠性均要大于工业级产品要求&#xff0c;…

优化改进YOLOv5算法之Shift-ConvNets,具有大核效应的小卷积核,效果提升明显

目录 1 Shift-ConvNets模块原理 1.1 Decomposition and Combination of Convolution 1.2 Sparse Dependencies of Large Convolution Kernels 1.3 Intermodule Feature Manipulation 2 YOLOv5中加入Shift-ConvNets模块 2.1 common.py文件配置 2.2 yolo.py配置 2.3 创建…

龙讯旷腾PWmat计算vdW异质结中热载流子冷却 | 复刻《Phys. Chem. Chem. Phys 》文献

01 NAMD 背景介绍 在各类光物理与光化学过程当中&#xff0c;均会牵涉到激发态载流子动力学过程&#xff0c;诸如电荷弛豫、复合以及输运等等。光激发或者电子注入将初始的平衡状态打破&#xff0c;所产生的热载流子在其演化进程中&#xff0c;会与原子核产生强烈耦合。此时&a…

Elsevier曝光73篇论文操纵同行评审,涉嫌操纵同行评审和人为引用

近日&#xff0c;全球知名学术出版商爱思唯尔&#xff08;Elsevier&#xff09;旗下期刊《Engineering Analysis with Boundary Elements》对73篇论文发出了关注声明&#xff0c;这些论文涉嫌操纵同行评审和人为引用。去年7月&#xff0c;该期刊前编委Masoud Afrand被指涉嫌论文…

OceanBase v4.2 特性解析:支持并发建表,提升OMS导入效率

背景 OceanBase 4.0版本新增了单日志流架构&#xff0c;使得OBServer单机突破了原有的分区数限制&#xff0c;支持更大数量的分区。 很多业务环境为了处理单机数据量过大的问题&#xff0c;通常采取分库分表的方法&#xff0c;这一方法会导致业务需要创建数十万乃至百万级别的…

电网铁塔安全:输电线路智能螺栓在线监测装置|远程了解螺栓异常情况

电网铁塔安全&#xff1a;输电线路智能螺栓在线监测装置|远程了解螺栓异常情况 在浩渺的天空下&#xff0c;银线如织&#xff0c;纵横交错&#xff0c;那是我们的输电线路&#xff0c;是点亮万家灯火的血脉。然而&#xff0c;这看似坚强的网络&#xff0c;实则也隐藏着许多不为…

新港海岸NCS8801 RGBLVDS转DPEDP,支持4K60HZ,成熟方案大批量出货产品,国产化认证

NCS8801描述&#xff1a; NCS8801S是一种低功耗的RGB/LVDS到显示端口/eDP转换器&#xff0c;专为智能手机、平板电脑、笔记本电脑等移动设备而设计。以支持高清DP/eDP显示器。NCS8801S支持4车道DP/eDP输出&#xff0c;这通常需要以60Hz的帧率支持QXGA&#xff08;2048*1536&am…

数据可视化作业二:中国城市地铁数据可视化

目录 作业要求 一、绘制每个城市站点数量柱状图&#xff08;降序排列&#xff09; 1.1 每个城市站点数量统计 1.1.1 代码展示 1.1.2 统计结果展示 1.2 柱状图绘制 1.2.1 代码实现 1.2.2 绘制结果 二、绘制上海市地铁线路站点数饼状图 2.1 数据处理 2.2 代码实现 2.3…

RabbitMQ详解-06RabbitMQ高级

1. 过期时间TTL 可以对消息设置预期的时间&#xff0c;在这个时间内都可以被消费者接收获取&#xff1b;过了之后消息自动被删除。RabbitMQ可以对消息和队列设置TTL。有以下两种设置方法&#xff1a; 通过队列属性设置&#xff0c;队列中所有消息都有相同的过期时间。对消息进…

STM32学习笔记(六)--引脚重映射详解

STM32F103C8T6引脚定义&#xff1a; 在STM32微控制器中&#xff0c;外设引脚的复用功能&#xff08;Alternate Function&#xff0c;AF&#xff09;有时会出现冲突&#xff0c;例如当USART2_CTS和TIM2_CH1同时需要使用相同的引脚时。此时&#xff0c;可以通过引脚重映射功能&am…

AI播客下载:The Gradient-AI前沿见解

The Gradient 是一个致力于让更多人轻松了解人工智能&#xff0c;并促进人工智能社区内讨论的组织。我们目前开展的项目包括 The Gradient 杂志、The Gradient 播客、The Update 通讯以及 Mastodon 实例 Sigmoid Social。 我们是一个由来自不同机构和公司的研究生、研究人员及…

三十分钟学会RabbitMQ

1、初识MQ 1.1 MQ是什么&#xff1f; MQ(message queue)&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO先入先出&#xff0c;只不过队列中存放的内容是message而已&#xff0c;还是一种跨进程的通信机制&#xff0c;用于上下游传递消息。在互联网架构中…

Wills Room Environment Dormitory Room Environment

有126个独特的网格。包括所有展示的资产和优质资产。具有良好的细节水平,并针对Gameready项目进行了优化。 艺术家Jonjo Hemmens创造的环境 如果想填充你的游戏环境或任何类型的虚拟制作级别,这里有一个包,你可以获得并使用它来得到高质量的视觉效果和优化的资产。 下载:​…

Unicorn批量模拟执行爆破实践

实践题目&#xff1a;CFI-CTF 2018 Automated Reversing 代码与附件地址&#xff1a;https://github.com/Airrcat/unicorn_loader 目标附件如&#xff1a; 每份附件的代码大致如下&#xff1a; 简单来说&#xff0c;程序会接收命令行参数并作一个字节的运算后与一固定值的字…