当前位置: 首页 > article >正文

【Java基础面试题043】BigDecimal为什么能保证精度不丢失?

回答重点

BigDecimal使用十进制来表示数值,而不是二进制浮点数表示法,这使得它能够精确地表示所有十进制数值,不需要任何转换或舍入。

而且BigDecimal是无限精度,可以表示任意精度的小数(受限于内存),因此不会动不动被舍入截断,也可以手动设置精度和舍入模式来控制计算的精度

BigDecimal内部使用两个字段存储数字,一个是整数部分intVal,另一个用来表示小数点的位置scale,避免了浮点数转化过程中可能的精度丢失

计算时通过整数计算,再结合小数点位置和设置的精度与舍入行为,控制结果精度,避免了有默认浮点数舍入导致的误差

简化版:

public class BigDecimal extends Number implements Comparable<BigDecimal> {
    private final BigInteger intVal;  // 存储整数部分
    private final int scale;          // 存储小数点的位置

    public BigDecimal(String val) {
        // 使用 BigInteger 来表示数值
        intVal = new BigInteger(val.replace(".", ""));
        scale = val.contains(".") ? val.length() - val.indexOf(".") - 1 : 0;
    }
}

例如:

BigDecimal bigDecimal = new BigDecimal(11.123);

inVal存储11122999999999332 ...

scale存储49,因为小数后有49位

如果是另外两种创建对象的方式,经过断点调试,inVal的值是null,scale是3

扩展知识

浮点数精度丢失的原因

  • 二进制浮点数表示:
    • Java使用IEEE 754标准来表示浮点数,这个标准用二进制来表示小数
    • 但有些十进制小数在二进制下是无限不循环小数(例如0.1在二进制中是0.0001100110011...),因此需要被截断或四舍五入
  • 有限的表示范围:
    • floatdouble都有固定的大小(float是32位,double是64位),这限制了它们能表示的精度和范围。有些数字在这些有限的位数内无法精确表示。
  • 舍入误差:
    • 由于浮点数的表示方式,进行算术运算时,可能会产生舍入误差。例如,0.1 + 0.2在理论上应该等于0.3,但在浮点数运算中可能得到0.30000000000000004

0.1 * 0.2

public class FloatPrecisionExample {
    public static void main(String[] args) {
        double a = 0.1;
        double b = 0.2;
        System.out.println(a * b); // 输出 0.020000000000000004
    }
}

由于double底层是二进制来表示十进制小数,不过double并不能精确表示0.1,0.2,所以0.1 * 0.2自然就产生了微笑的误差

使用BigDecimal

import java.math.BigDecimal;

public class BigDecimalPrecisionExample {
    public static void main(String[] args) {
        BigDecimal a = new BigDecimal("0.1");
        BigDecimal b = new BigDecimal("0.2");
        System.out.println(a.multiply(b));  // 输出 0.02
    }
}

0.1 * 0.2底层大概是这么处理的:

0.1:intVal = 1        scale = 1

0.2:intVal = 2        scale = 1

先1 x 2 = 2

再标小数点位置2 -> 0.02


http://www.kler.cn/a/456747.html

相关文章:

  • 【PCIe 总线及设备入门学习专栏 4.2 -- PCI 总线的三种传输模式 】
  • STM32配合可编程加密芯片SMEC88ST的防抄板加密方案设计
  • vue 嵌套el-dialo,当内层的弹窗弹出时,整个页面被遮罩
  • 【YashanDB知识库】启动yasom时报错:sqlite connection error
  • python 渗透开发工具之SQLMapApi Server不同IP服务启动方式处理 解决方案SqlMapApiServer外网不能访问的情况
  • Docker-构建自己的Web-Linux系统-镜像webtop:ubuntu-kde
  • STM32学习之EXTI外部中断(以对外式红外传感器 / 旋转编码器为例)
  • 【087】基于51单片机智能宠物喂食器【Proteus仿真+Keil程序+报告+原理图】
  • 如何使用 Firewalld 设置 Linux 防火墙
  • pyQT + OpenCV相关练习
  • 【最新】沃德协会管理系统源码+uniapp前端+环境教程
  • linux中,redis分布式集群搭建
  • Android MQTT关于断开连接disconnect报错原因
  • 计算机网络|数据流向剖析与分层模型详解
  • 如何设置在启动时进入命令行模式彻底删除中文输入法。然后重启仍然是图形界面?
  • mysql的安装下载
  • Debian操作系统相对于Ubuntu有什么优势吗?
  • 集成RabbitMQ+MQ常用操作
  • redis相关数据类型介绍
  • .NET常用的ORM框架及性能优劣分析总结
  • 梳理你的思路(从OOP到架构设计)_介绍Android的Java层应用框架03
  • Web Bluetooth API 开发记录
  • 常见局域网硬件故障诊断
  • linux驱动:6ull(5)自定义根节点
  • flink cdc使用flink sql方式运行一直报Make sure a planner module is on the classpath
  • Linux下基本指令