BigDecimal(double)和BigDecimal(String)有什么区别?

news2024/11/13 9:34:48

面试回答

有区别,而且区别很大。

因为double是不精确的,所以使用一个不精确的数字来创建 BigDecimal,得到的数字也是不精确的。如 0.1 这个数字,double 只能表示他的近似值。

所以,当我们使用 new BigDecimal(0.1) 创建一个 BigDecimal 的时候,其实创建出来的值并不是正好等于 0.1 的。

而是 0.1000000000000000055511151231257827021181583404541015625。这是因为 double 自身表示的只是一个近似值。

而对于 BigDecimal(String),当我们使用 new BigDecimal("0.1")创建一个 BigDecimal 的时候,其实创建出来的值正好就是等于 0.1 的。

那么他的标度也就是 1。

知识扩展

在《阿里巴巴Java开发手册》中有一条建议,或者说是要求:

12.【强制】禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象。

说明:BigDecimal(double) 存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。

如:BigDecimal g=new BigDecimal(0.1F); 实际的存储值为:0.100000001490116119384765625

正例:优先推荐入参为 String 的构造方法,或使用 BigDecimal 的 valueOf 方法,此方法内部其实执行了 Double 的 toString,而 Double 的 toString 按 double 的实际能表达的精度对尾数进行了截断。

BigDecimal recommend1 = new BigDecimal("0.1");

BigDecimal recommend2 = new BigDecimal.valueOf(0.1);

BigDecimal 如何精确计数?

如果大家看过 BigDecimal 的源码,其实可以发现,实际上一个 BigDecimal 是通过一个 “无标度值”和一个“标度”来表示一个数的。

在 BigDecimal 中,标度是通过 scale 字段来表示的。

而无标度值的表示比较复杂。当 unscaled value 超过阈值(默认为 Long.MAX_VALUE)时采用 intVal 字段存储 unscaled value,intCompact 字段存储 Long.MIN_VALUE,否则对 unscaled value 进行压缩存储到 long 型的 intCompact 字段用于后续计算,intVal 为空。

涉及到的字段就是这几个:

public class BigDecimal extends Number implements Comparable<BigDecimal> {
    /**
     * The unscaled value of this BigDecimal, as returned by {@link
     * #unscaledValue}.
     *
     * @serial
     * @see #unscaledValue
     */
    private final BigInteger intVal;

    /**
     * The scale of this BigDecimal, as returned by {@link #scale}.
     *
     * @serial
     * @see #scale
     */
    private final int scale;  // Note: this may have any value, so
                              // calculations must be done in longs

    /**
     * If the absolute value of the significand of this BigDecimal is
     * less than or equal to {@code Long.MAX_VALUE}, the value can be
     * compactly stored in this field and used in computations.
     */
    private final transient long intCompact;

}

关于无标度值的压缩机制大家了解即可,不是本文的重点,大家只需要知道 BigDecimal 主要是通过一个无标度值和标度来表示的就行了。

那么标度到底是什么呢?

除了 scale 这个字段,在 BigDecimal 中还提供了 scale() 方法,用来返回这个 BigDecimal 的标度。

    /**
     * Returns the <i>scale</i> of this {@code BigDecimal}.  If zero
     * or positive, the scale is the number of digits to the right of
     * the decimal point.  If negative, the unscaled value of the
     * number is multiplied by ten to the power of the negation of the
     * scale.  For example, a scale of {@code -3} means the unscaled
     * value is multiplied by 1000.
     *
     * @return the scale of this {@code BigDecimal}.
     */
    public int scale() {
        return scale;
    }

那么,scale 到底表示的是什么,其实上面的注释已经说的很清楚了:

如果 scale 为零或正值,则该值表示这个数字小数点右侧的位数。如果 scale 为负数,则该数字的真实值需要乘以 10 的该负数的绝对值的幂。例如,scale 为 -3,则这个数需要乘 1000,即在末尾有3个0。

如 123.123,那么如果使用 BigDecimal 表示,那么他的无标度值为 123123,他的标度为 3。

而二进制无法表示的 0.1,使用 BigDecimal 就可以表示了,及通过无标度值1和标度1来表示。

我们都知道,想要创建一个对象,需要使用该类的构造方法,在 BigDecimal 中一共有以下 4 个构造方法:

BigDecimal(int val) 
BigDecimal(double val) 
BigDecimal(long val) 
BigDecimal(String val) 

以上四个方法,创建出来的 BigDecimal 的标度(scale)是不同的。

其中 BigDecimal(int) 和 BigDecimal(double) 比较简单,因为都是整数,所以他们的标度都是0。

而 BigDecimal(double) 和 BigDecimal(String) 的标度就有很多学问了。

BigDecimal(double) 有什么问题?

BigDecimal 中提供了一个通过 double 创建 BigDecimal 的方法——BigDecimal(double),但是,同时也给我们留了一个坑!

因为我们知道,double 表示的小数是不精确的,如 0.1 这个数字,double 只能表示他的近似值。

所以,当我们使用 new BigDecimal(0.1) 创建一个 BigDecimal 的时候,其实创建出来的值并不是正好等于 0.1 的。

而是 0.1000000000000000055511151231257827021181583404541015625。这是因为 double 自身表示的只是一个近似值。

所以,如果我们在代码中,使用 BigDecimal(double) 来创建一个 BigDecimal 的话,那么是损失了精度的。这是极其严重的。

使用 BigDecimal(String)创建

那么,该如何创建一个精确的 BigDecimal 来表示小数呢,答案是使用 String 创建。

而对于 BigDecimal(String),当我们使用 new BigDecimal("0.1")创建一个 BigDecimal 的时候,其实创建出来的值正好就是等于 0.1 的。

那么他的标度也就是1。

但是需要注意的是,new BigDecimal("0.10000") 和 new BigDecimal("0.1") 这两个数的标度分别是 5和1,如果使用 BigDecimal 的 equals 方法比较,得到的结果是 false。

那么,想要创建一个能精确的表示 0.1 的 BigDecimal,请使用一下两种方式:

        BigDecimal recommend1 = new BigDecimal("0.1");
        BigDecimal recommend2 = BigDecimal.valueOf(0.1);

总结

因为计算机采用二进制处理数据,但是很多小数,如 0.1的二进制是一个无限循环小数,而这种数字在计算机中是无法精确表示的。

所以,人们采用了一种通过近似值的方式在计算机中表示,于是就有了单精度浮点数和双精度浮点数等。

所以,作为单精度浮点数的 float 和双精度浮点数的 double,在表示小数的时候只是近似值,并不是真实值。

所以,当使用 BigDecimal(Double) 创建一个数的时候,得到的 BigDecimal 是损失了精度的。

而使用一个损失了精度的数字进行计算,得到的结果也是不精确的。

想要避免这个问题,可以通过 BigDecimal(String) 的方式创建 BigDecimal,这样的情况下,0.1 就会被精确的表示出来。

其表现形式是一个无标度数值1,和一个标度1的组合。

 

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

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

相关文章

SwiftUI 兼容 Light Dark

1. Assets 右键 New Color Set 2. 起个合适的颜色名称&#xff0c;修改一下 demo_bgcolordemo_card_bgcolordemo_text_color 3. Show Me The Code // // light_dark_demo.swift // bill2 // // Created by 朱洪苇 on 2023/8/10. //import SwiftUIstruct light_dark_demo: …

机器学习---自编码器

自编码器过程 输入一个图片&#xff0c;经过encoder变成一个向量&#xff0c;再通过decoder将这个向量反向生成输入的图片。 这里我们希望输入和输出越接近越好。这个过程我们称为重建。 特点&#xff1a;不需要任何的标注资料。 在2006年这个思想就被提出来了&#xff1a; …

消息队列比较

、ActiveMQ 优点&#xff1a;单机吞吐量万级&#xff0c;时效性ms级&#xff0c;可用性高&#xff0c;基于主从架构实现高可用性&#xff0c;消息可靠性较低的概率丢失数据。 缺点&#xff1a;官方社区现在对ActiveMQ5.X维护越来越少了&#xff0c;高吞吐量场景较少使用。 2、K…

《Linux从练气到飞升》No.10 冯洛依曼体系结构

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的…

rust关于项目结构包,Crate和mod和目录的组织

rust 最近开始学习rust语言。感觉这门语言相对java确实是难上很多。开几个文章把遇到的问题记录一下 rust关于包&#xff0c;Crate 关于包&#xff0c;Crate这块先看看官方书籍怎么说的 crate 是 Rust 在编译时最小的代码单位。如果你用 rustc 而不是 cargo 来编译一个文件…

Opencv实战(银行卡识别)

目录 模版匹配不同方法对比一个模板匹配多个 直方图及直方图均衡化Mask操作直方图均衡化自适应直方图均衡化 模版匹配 公式 import cv2 #opencv读取的格式是BGR import numpy as np import matplotlib.pyplot as plt#Matplotlib是RGB %matplotlib inline def cv_show(img,nam…

win10 + VS2022 安装opencv C++

最近需要用到C opencv&#xff0c;看了很多帖子都需要自己编译opencv源码。为避免源码编译&#xff0c;可以使用VS来配置opencv C。下面是主要过程&#xff1a; 目录 1. 从官网下载 opencv - Get Started - OpenCV 2. 点击这个exe文件进行安装 3. 配置环境变量 4. VS中的项…

【数据结构与算法】多路查找树

多路查找树 二叉树的问题分析 二叉树的操作效率较高&#xff0c;但也存在问题。 二叉树需要加载到内存的&#xff0c;如果二叉树节点少&#xff0c;没什么问题&#xff0c;但是如果二叉树的节点很多&#xff08;比如 1 亿&#xff09;&#xff0c;就存在如下问题&#xff1a…

【信号生成器】从 Excel 数据文件创建 Simulink 信号生成器块研究(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

深度学习:使用卷积神经网络CNN实现MNIST手写数字识别

引言 本项目基于pytorch构建了一个深度学习神经网络&#xff0c;网络包含卷积层、池化层、全连接层&#xff0c;通过此网络实现对MINST数据集手写数字的识别&#xff0c;通过本项目代码&#xff0c;从原理上理解手写数字识别的全过程&#xff0c;包括反向传播&#xff0c;梯度…

selenium爬虫,配置谷歌浏览器的driver

用selenium爬虫时&#xff0c;明明已经安装了selenium模块&#xff0c;程序却运行不了 本文主要涉及驱动有问题driver 网上有很多手动的方法&#xff08;查看谷歌浏览的版本然后在其他博主分享的webdriver中下载与自己谷歌版本号最贴近的版本号&#xff0c;并把webdriver的地址…

AtuoDL----Tensorboard可视化使用

本教程教你怎么在autodl上使用tensorboard。 1、保存tensorboard日志文件 修改保存日志文件的路径&#xff0c;只有在这个tf-logs下的日志文件才能显示 2、查看tensorboard 进入AutoPanel&#xff0c;点击Tensorboard就能查看

跟禹神VUE——组件间的通信方式(props配置项、组件间自定义事件、全局事件总线、消息订阅与发布、VUEX)

一、通过props配置项传递数据&#xff08;适用于父组件给子组件传递数据&#xff09; 父组件向子组件传递数据&#xff1a; 父组件代码&#xff1a;在子组件的标签中传递数据 <template><div><h2>学校名称&#xff1a;{{schoolName}}</h2><!-- 方…

ROS入门核心教材重要节选

ROS核心教程 1、文件系统 使用下述命令查看包 rospack ros pack(age&#xff09; 如rospack find roscpp roscd ros cd 如roscd roscpp rosls ros ls 如rosls roscpp2、ROS节点 节点可以理解为人工定义一个机器人模块&#xff0c;然后抽象成可执行文件。 rosnode li…

改进DevSecOps框架的 5 大关键技术

Markets and Markets的一项研究显示&#xff0c;全球DevOps的市场规模从2017年的29亿美元增加到2023年的103.1亿美元&#xff0c;预测期的年复合增长率(CAGR)为24.7%。人们对DevOps越来越感兴趣&#xff0c;因为DevOps不仅能够压缩软件的交付周期&#xff0c;还能提高交付的速度…

7.8 封装详解

7.8 封装详解 就是把东西装进箱子里&#xff0c;只留一个口&#xff0c;比如我们看电视的时候我们只用遥控器换一个台就行了&#xff0c;不需要知道电视里面是怎么构造的&#xff0c;电视机使用的厂家为了使用方便就把电视机内部的组件全部封装在了壳子里&#xff0c;只给我们…

Web-WebApp Vue.js 目录结构

WebApp Vue.js 目录结构 目录解析 目录/文件 说明 build 最终发布的代码存放位置。config 配置目录&#xff0c;包括端口号等。我们初学可以使用默认的。node_modules npm 加载的项目依赖模块 src 这里是我们要开发的目录&#xff0c;基本上要做的事情都在这个目录里。里面包…

剪切、复制、粘贴事件

剪切、复制、粘贴事件 oncopy 事件在用户拷贝元素上的内容时触发。onbeforecut 事件在用户剪切文本&#xff0c;且文本还未删除时触发触发。oncut 事件在用户剪切元素的内容时触发。onbeforepaste 事件在用户向元素中粘贴文本之前触发。onpaste 事件在用户向元素中粘贴文本时触…

(2023Arxiv)Meta-Transformer: A Unified Framework for Multimodal Learning

论文链接&#xff1a;https://arxiv.org/abs/2307.10802 代码链接&#xff1a;https://github.com/invictus717/MetaTransformer 项目主页&#xff1a;https://kxgong.github.io/meta_transformer/ 【注】&#xff1a;根据实验结果来看&#xff0c;每次输入一种数据源进行处…

【位操作符的几种题型】

位操作符的几种题型 目录 题型一&#xff1a;寻找“单身狗”。 题型二&#xff1a;计算一个数在二进制中1的个数 题型三&#xff1a;不允许创建临时变量&#xff0c;交换两个整数的内容 题型一&#xff1a;寻找“单身狗”。 1.1题目解析 在一个整型数组中&#xff0c;只有…