【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 testnew 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.8BigDecimal 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.25BigDecimal 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.mzph.cn/news/856141.shtml

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

相关文章

真空玻璃可见光透射比检测 玻璃制品检测 玻璃器皿检测

建筑玻璃检测 防火玻璃、钢化玻璃、夹层玻璃、均质钢化玻璃、平板玻璃、中空玻璃、真空玻璃、镀膜玻璃夹丝玻璃、光栅玻璃、压花玻璃、建筑用U形玻璃、镶嵌玻璃、玻璃幕墙等 工业玻璃检测 钢化安全玻璃、电加温玻璃、玻璃、半钢化玻璃、视镜玻璃、汽车安全玻璃、汽车后窗电热…

Walrus:去中心化存储和DA协议,可以基于Sui构建L2和大型存储

Walrus是为区块链应用和自主代理提供的创新去中心化存储网络。Walrus存储系统今天以开发者预览版的形式发布&#xff0c;面向Sui开发者征求反馈意见&#xff0c;并预计很快会向其他Web3社区广泛推广。 通过采用纠删编码创新技术&#xff0c;Walrus能够快速且稳健地将非结构化数…

数据库选型实践:如何避开分库分表痛点 | OceanBase用户实践

随着企业业务的不断发展&#xff0c;数据量往往呈现出快速的增长趋势。使用MySQL的用户面对这种增长&#xff0c;普遍选择采用分库分表技术作为应对方案。然而&#xff0c;这一方案常在后期会遇到很多痛点。 分库分表的痛点 痛点 1&#xff1a;难以保证数据一致性。由于分库分…

CCNA 0基础入门

OSI & TCP/IP OSI参考模型 TCP/IP协议 应用层 ------↓表示层 ------>应用层会话层 ------↑传输层 ------>传输层网络层 ------>网络互联层链路层 ------>网络接口层物理层 ------>↑ 物理层 传输的信号以及网线以及接线 主要作用是产生并检测电…

【python中的turtle库有哪些常用的方法】

turtle库是Python中一个非常流行的图形绘制库&#xff0c;用于绘制简单的图形和进行基础的动画编程。以下是turtle库中常用的一些方法&#xff1a; 画笔控制 penup(): 抬起画笔&#xff0c;移动时不留痕迹。pendown(): 放下画笔&#xff0c;移动时留下痕迹。pensize(width): …

高压电阻器支持牙科 X 射线成像的准确性

为了捕获患者牙齿和颌骨的足够图像&#xff0c;牙医依靠锥形束计算机断层扫描 &#xff08;CBCT&#xff09; 系统的先进 3D 成像。CBCT系统的输出对于准确诊断口腔健康问题和随后的治疗计划至关重要。为了确保这些图像的可靠性&#xff0c;CBCT系统制造商利用了Exxelia Ohmcra…

数据库 |试卷八试卷九试卷十

1.基数是指元组的个数 2.游标机制 3.触发器自动调用 4.count(*)统计所有行&#xff0c;不忽略空值null&#xff0c;但不但要全局扫描&#xff0c;也要对表的每个字段进行扫描&#xff1b; 5.eacherNO INT NOT NULL UNIQUE&#xff0c;为什么不能断定TeacherNO是主码&#xff…

Samtec制造理念系列一 | 差异变量的概念

【摘要/前言】 制造高端电子产品是非常复杂精密的过程。制作用于演示或原型的一次性样品可能具有挑战性&#xff0c;但真正的挑战在于如何以盈利的方式持续生产。 这就是Samtec风险投资研发工程总监Aaron Tucker在一次关于生产高密度微小型连接器的挑战的演讲中所强调的观点。…

游戏心理学Day19

玩家类型 玩家类型学的优势在于让设计师利用类型学原理&#xff0c;分析玩家心理&#xff0c;进而设计出相应的游戏和玩法。不过这类定制化选项还不少&#xff0c;不只是因为他们需要复杂的编程技术&#xff0c;还因为他们要求设计师详细描述极具弹性的玩家个性模型。这一模型…

基于Gunicorn、Flask与Docker实现高并发Web应用部署教程

在当今互联网高速发展的时代&#xff0c;高并发成为了Web应用必须要面对的挑战之一。为了应对这一挑战&#xff0c;我们可以利用Gunicorn、Flask和Docker等强大的工具来构建和部署高并发的Web应用。本文将详细介绍如何使用这些工具来构建一个高效、稳定的Web应用&#xff0c;并…

Docker+MySQL:打造安全高效的远程数据库访问

在现代应用开发和部署中&#xff0c;数据库是关键组件之一。无论是开发环境还是生产环境&#xff0c;快速、可靠地部署和管理数据库都是开发人员和运维人员面临的常见挑战之一。 Docker是一种流行的容器化技术&#xff0c;它使得应用程序的部署和管理变得非常简单和高效。通过使…

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

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

C++ | Leetcode C++题解之第155题最小栈

题目&#xff1a; 题解&#xff1a; class MinStack {stack<int> x_stack;stack<int> min_stack; public:MinStack() {min_stack.push(INT_MAX);}void push(int x) {x_stack.push(x);min_stack.push(min(min_stack.top(), x));}void pop() {x_stack.pop();min_sta…

服务器:服务器资源信息采集清单推荐

服务器基本信息采集表设备名称厂商型号规格数量质保服务器机箱主板CPU内存条阵列卡SATA SSD系统盘3.5SATA数据盘硬盘背板电源模组网卡Riser卡加速卡算力卡电源线风扇后置硬盘U.2盘主板内嵌M.2盘总功耗操作系统

Flutter第十四弹 抽屉菜单效果

目标&#xff1a; 1.怎么构建抽屉菜单效果&#xff1f; 2.抽屉菜单怎么定制&#xff1f; 一、抽屉菜单 侧滑抽屉菜单效果 1.1 抽屉菜单入口 Flutter 的脚手架Scaffold&#xff0c;默认提供了抽屉菜单效果入口。 主页面采用一个简单的页面&#xff0c;侧滑菜单首先使用一个I…

算法训练营64-图论-深度优先优先搜索(dfs)-广度优先搜索(bfs)

题目&#xff1a;98. 所有可达路径 (kamacoder.com) 邻接矩阵表示图 #include<bits/stdc.h>using namespace std;vector<vector<int>> reslut; vector<int> path;void dfs(vector<vector<int>>& map, int start, int end) {if(start…

适合营销的叙事可视化

背景 数据可视化与数据故事化的差异和相似点&#xff0c;以及它们如何协同工作&#xff0c;将你的数据转化为清晰、简洁、可操作的信息&#xff0c;以便您的组织使用。 什么是数据可视化&#xff1f; 数据可视化通过图像传达信息——这是你所收集数据的视觉表示。通过提供原…

vue3使用Threejs不生效找不到THREE,换个版本试试

一、版本 “three”: “^0.152.2”, 二、3D粒子波浪效果 <template><div class"LatheBufferGeometry"></div> </template> <script> import * as THREE from "three" export default {name: LatheBufferGeometry,setup()…

C++11包装器function

知识回顾&#xff1a; 在C中我们要调用一个函数是需要用到函数指针 在C中我们调用一个函数有两种方法。1.仿函数。2.lambda 多种方式在调用时&#xff0c;就会出现多种情况&#xff0c;为方便接收&#xff0c;C11引出包装器的概念 std::function类模板函数是一个通用的可调用…

解决 ModuleNotFoundError: No module named

解决 ModuleNotFoundError: No module named &#x1f4bb; 解决 ModuleNotFoundError: No module named摘要引言正文内容&#x1f914; 报错问题解决思路解决方法1. 确认模块是否已安装2. 确认模块是否在当前Python环境中可用3. 检查模块的导入名称4. 检查Python路径5. 检查操…