bigdecimal取小数部分_小数精度丢失问题分析和解决

无论在什么业务中,钱?是非常重要的东西,对账的时候一定要对的上,不能这边少一分钱那边多一分钱。对于数值的计算,尤其是小数,floatedouble都是禁止使用的。

阿里强制要求存放小数时使用 decimal,禁止使用 float 和 double。

说明:float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。

处理方式可以为:mysql 可以用 decimal ,如果你是用 java, 在商业计算中我们要用 java.math.BigDecimal,注意:如果需要精确计算,非要用String来构造BigDecimal不可!

那么到底是什么情况?为什么我们的账户一会少一分一会多一分(往往是少一分6167e0ad6826f18ac30675aeded79bc8.png),如何解决呢?

2baa6f1c8d162b5c21fa4e16312bb6a6.png

一个例子说明

废话不多说,当我们拿着一块钱去买了一根9毛的棒冰会发生啥?本来只剩1毛钱就不多了,老板还扣我0.000....0002分?上图:

4e944b940ef9273870c22c95468ee97a.png

问题原因

无论是我们本文提到的double,还是float,都是浮点数。

在计算机科学中,浮点(英语:floating point,缩写为FP)是一种对于实数的近似值数值表现法,由一个有效数字(即尾数)加上幂数来表示,通常是乘以某个基数的整数次指数得到。以这种表示法表示的数值,称为浮点数(floating-point number)。

划重点,⭐⭐⭐其实我觉得很好理解,我们之前说过,计算机计算加减乘除啊,都是用的加法器,实质都是二进制的加法处理。那么这里就有一个二进制表示的问题。试想,4,2,8之流都是2的幂次方,可以完美用二进制表示,计算当然不会出现问题。对于0,1,3,5之类也都可以用二进制来表示出来,所以,整数肯定是没问题的。

但是对于小数呢?1(2的0次方)、0.5(2的-1次方)、0.25(2的-2次方)、0.75(2的-1次方+2的-2次方),那都是可以转换成二进制的小数:

58b964bfddc36ee91ad231ff7c532124.png

但是如十进制的0.1,就无法用二进制准确的表示出来(你用2的次方来凑凑?)。因此只能使用近似值的方式表达。如果我们尝试着把10进制的0.1转化成二进制,会怎么转呢?

在十进制中,0.1如何计算出来的呢?

0.1 = 1 ÷ 10

那么二进制中也是同理:

1 ÷ 1010

我们回到小学的课堂,来列竖式吧:

       0.000110011...
------------------
1010 ) 1 0000
1010
------
1100
1010
----
10000
1010
-----
1100
1010
----
10

很显然,除不尽,除出了一个无限循环小数:二进制的 0.0001100110011...

有的同学表示怀疑?这结果正确?

我写在这里当然正确啦,前面标注了是二进制,小数点后面一位就是-1次方依次计算,我们的0.1是不是介于(2的-3次方)和(2的-4次方)之间,那么显然是从小数点第四个开始有1。

好了,那么,如何在计算机中表示这个无限不循环的小数呢?只能考虑按照不同的精度保留不同的位数。

我们知道float是单精度的(JAVA中是32位),double是双精度的(JAVA中是64位)。不同的精度,其实就是保留的有效数字位数不同,保留的位数越多,精度越高。

所以,浮点数在Java中是无法精确表示的,因为大部分浮点数转换成二进制是一个无限不循环的小数,只能通过保留精度的方式进行近似表示。

问题的解决

String 构造方法是完全可预知的:写入 newBigDecimal("0.1") 将创建一个 BigDecimal,它正好等于预期的 0.1。因此,比较而言,通常建议优先使用String构造方法。

使用BigDecimal(String val)

//加法
public static BigDecimal add(double v1, double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2);
}

//减法
public static BigDecimal sub(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2);
}

//乘法
public static BigDecimal mul(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2);
}

//除法
public static BigDecimal div(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2,2,BigDecimal.ROUND_HALF_UP);//四舍五入,保留2位小数,应对除不尽的情况
}

那么,上面的精度丢失问题就迎刃而解了。但是除不尽怎么办?比如10.0除以这里的3.0,保留小数点后三位有效数字:

d40b62c449fdf4c8d0851319575cb68d.png

那么,每个用户得到的都是3.333元,三个用户加起来是得不到10块钱的。

对于除法,始终会产生除不尽的情况怎么办?有个词叫轧差

什么意思呢?举个简单例子。假如现在需要把10元分成3分,如果是10除以3这么除,会发现为3.33333无穷尽的3。这些数字完全无法在程序或数据库中进行精确的存储。

简单理解就是,当除不尽或需去除小数点的时候,前面的n-1笔(这里n=3)做四舍五入。最后一笔做兜底(总金额减去前面n-1笔之和)。这样保证总金额的不会丢失。

比如10块钱,三个用户分,前面两个用户只能各分到3.333块钱,最后一个用户分到3.334块钱。保证总额不变。是不是感觉很机智5276513ca9fef6dcd37a77ed40dcf289.png5276513ca9fef6dcd37a77ed40dcf289.png5276513ca9fef6dcd37a77ed40dcf289.png

好了,我们可以准确地管理我们的钱了。不过针对这个钱的问题,更机智的我选择保存钱的时候用分为单位来操作和保存4c15f98cd0e69e9ec8044035e3c681af.png4c15f98cd0e69e9ec8044035e3c681af.png4c15f98cd0e69e9ec8044035e3c681af.png,能少一事就少一事吧。当然了,有的时候必须用到小数的时候,请记住我们该如何使用哦9809624f899019f301a1207cba5f9645.png9809624f899019f301a1207cba5f9645.png9809624f899019f301a1207cba5f9645.png~

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

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

相关文章

转收藏:Git常用命令速查表

一、 Git 常用命令速查 git branch 查看本地所有分支git status 查看当前状态 git commit 提交 git branch -a 查看所有的分支git branch -r 查看远程所有分支git commit -am "init" 提交并且加注释 git remote add origin git192.168.1.119:ndshowgit push origin m…

php curl ajax get请求,PHP的curl的get,post请求-Fun言

GET请求如下:/** param string $url* return mixed*/public function doGet($url){//初始化$ch curl_init();curl_setopt($ch, CURLOPT_URL,$url); // 执行后不直接打印出来curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);curl_setopt($ch, CURLOPT_HEADE…

java运行库一键修复_在运行时修补Java

java运行库一键修复本文将重点介绍如何解决与第三方库相关的问题 不能被规避 难以排除/绕过/更换 只需不提供错误修正 在这种情况下,解决问题仍然是一项艰巨的任务。 作为这种情况的诱因,请考虑对“哈希索引”数据结构的攻击,例如java.ut…

使用LinkedList模拟一个堆栈或者队列数据结构

使用LinkedList模拟一个堆栈或者队列数据结构。 堆栈:先进后出 如同一个杯子。 队列:先进先出 如同一个水管。 import java.util.LinkedList;public class DuiLie {private LinkedList link;public DuiLie() {link new LinkedList();}public void m…

k40游戏增强版服务器维护中,Redmi K40 游戏增强版发布,第四台 K40 出现了

原标题:Redmi K40 游戏增强版发布,第四台 K40 出现了今晚 Redmi 发布了旗下的新款游戏手机 —— Redmi K40 游戏增强版,这也是K40、K40 Pro、K40 Pro 之后的又一款 K40 产品,主打的是专游戏功能和轻薄设计。Redmi K40 游戏增强版使…

mpvue微信小程序动画_入门微信小程序

为何现在的微信小程序还是高温不退?主要原因如下:无需安装、不占内存、易传播。废话不多说,开始进入开发!-----------小程序环境搭建-----------------------------------账号注册百度搜索 "微信公众平台"官网地址&…

瀑布流式布局

今天终于搞懂了瀑布流式布局,哈哈,总结下 瀑布流式布局分为两种类型:1、每一列都限定宽度不限定高度的布局(使用浮动)2、宽度不是写死的,是根据页面的放大缩小变化的(定位布局) 瀑布…

Spock VW:编写自定义的Spock框架扩展

Spock框架具有多个内置扩展 ,这些扩展支持许多核心功能,例如Ignore和Timeout批注。 但更重要的是,鼓励开发人员编写自己的扩展。 例如, SpringExtension很好地将Spock与Spring框架集成在一起。 编写自定义扩展没有很好的文档记录。…

物理服务器备份系统,物理备份和逻辑备份区别

物理备份和逻辑备份区别 内容精选换一换可能这份面试题还不足以包含所有Java问题,但有了它,我相信你一定不会“败”的很惨,有了它,足以应对目前市面上绝大部分的Java面试了,因为这些问题不论是从深度还是广度上来讲&am…

dd命令iso linux_BootISO:从 ISO 文件中创建一个可启动的 USB 设备

今天,我们将讨论名为 BootISO 的实用程序类似工具。它是一个简单的 bash 脚本,允许用户来从 ISO 文件中创建一个可启动的 USB 设备。-- Prakash Subramanian(作者)为了安装操作系统,我们中的大多数人(包括我)经常从 ISO 文件中创建一个可启动…

java基础-接口

转载于:https://www.cnblogs.com/ceshi2016/p/6025027.html

Python之路,day4-Python基础

1.集合2.元组 只读列表,只有count,index2个方法3.字典key-value对 1.特性 2.查询速度快,比列表快python中的hash在同一程序下值相同python字典中的hash只有key是hash的hash之后二分查找,劈半劈半注:只有unicode有…

gwt入门和进阶_GWT入门

gwt入门和进阶GWT是Google Web Development Kit的缩写,可让程序员使用Java开发Ajax Web应用程序。 GWT编译器将Java代码转换为JavaScript和html代码。 GWT应用程序称为模块,并且使用xml文件描述模块,假设该模块名称为xml文件的“ mymodule”名…

workbook加载文件路径_通过Workbook.XML 修复Excel自定义名称

小伙伴们经常想求助IT 提升打开Excel的速度, 标准回答是:重启。其实Excel 中影响打开速度的几个因素:1. 公式 2. 链接 3. 自定义名称 自定义名称常常被人忽视,里面经常隐藏着众多错误,而且有很多的名称是隐藏的&#x…

将IDE检查应用于自定义Java批注

J2SE 5中注释的引入改变了我们编写和处理Java的方式。 除了Java SE的预定义注释外 ,框架,IDE和工具包还引入了自己的自定义注释 。 Checker框架提供了一些示例,说明如何使用自定义注释在Java中增加类型安全性 。 在本文中,我着眼于…

有什么用_app用什么软件编写

自己咋开发APP这得看你的学习程度了,如果你学了安卓开发那么久按照教程来吧,如果没有学过,那么就看看我的回答是不是贴题意的。比较快的开发app方式。接入任意后台,通过HBuilder封装成app。2.使用MUI,借用官方的组件代…

java函数的笔记

java函数的笔记 java中,函数即方法。也就是实现某个功能的办法。 函数的格式 修饰符 返回值类型 函数名(参数类型 参数) { 逻辑处理; return 处理结果; // return关键字是用于结束该函数的,并将处理结果返回给调用者。void类型可以省略return&#xff0…

jquery手写轮播图_15个超强的jQuery/HTML5图片轮播插件

最近我们为大家分享过不少基于jQuery的图片轮播插件,当然也有很多使用了HTML5和CSS3的相关技术,让整个图片播放器显得更加美观,动画效果显得更加炫酷。这次我们特意为大家筛选了一些最新的jQuery/HTML5图片轮播插件,每一个的功能都…

NLOG配置

<?xml version"1.0" encoding"utf-8" ?><nlog xmlns"http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"> <!-- See https://github.com/nlog/nlog/wiki/Config…

gwt格式_GWT –利弊

gwt格式我喜欢JavaScript。 随着jQuery和Mootools的出现&#xff0c;我对JavaScript的热爱倍增 。 如果有选择&#xff0c;我将对开发的任何Web应用程序使用上述任一框架。 但是进入服务行业后&#xff0c;我不得不一次次屈服于客户的压力&#xff0c;并在他们选择技术的过程中…