浮点数值计算精度丢失问题剖析及解决方法
文章目录
- 1、原因分析
- 2、解决方法
- 2.1、Java中使用 BigDecimal 类
- 2.2、JavaScript 中解决计算精度丢失的问题
- 3、使用建议
1、原因分析
首先我们来看个反直觉的浮点数值计算
System.out.println(0.3*3);

有的同学可能要问为啥不是0.9?
首先要知道为什么会产生这个问题?我们知道计算机的底层世界都是由0和1组成的,而浮点数值就是采用二进制系统表示,常见两种基本的浮点类型: float 和 double。
其中单精度float为32位浮点数,1位符号,8位指数和23位尾数(小数部分)。

双精度double则为64位浮点数,1位符号,11位指数和52位尾数(小数部分)。
接下来我们先看下十进制小数转二进制的例子,例如将 0.3 转为二进制
0.3*2=0.6 //取整数0
0.6*2=1.2 //取整数1
0.2*2=0.4 //取整数0
0.4*2=0.8 //取整数0
0.8*2=1.6 //取整数1
0.6*2=1.2 //取整数1
0.2*2=0.4 //取整数0
0.4*2=0.8 //取整数0
0.8*2=1.6 //取整数1
......
二进制表示为:010011001......
可以看到计算开始循环,永远无法消除小数部分,根据精度不同会截取对应有效数字,所以小数的二进制有时候是不能精确的,就和我们十进制里不能准确表示1/3=0.33333333…是一个道理。
这种情况在计算时会造成了精度丢失,也就是舍入误差,对于金额计算会产生严重的后果。
2、解决方法
2.1、Java中使用 BigDecimal 类
我们先看下Java里面的BigDecimal类,构造方法如下


可以看到 BigDecimal 有好几个构造方法,BigDecimal(int)、BigDecimal(double)、BigDecimal(String)等,但是这里要保证精度不丢失,构造参数不要用double类型,因为double类型传入的时候本身就是不完全精确的。如下:
BigDecimal bd1=new BigDecimal("0.3");
BigDecimal bd2=new BigDecimal("3");
BigDecimal bd3=new BigDecimal(3);
System.out.println(bd1.multiply(bd2));
System.out.println(bd1.multiply(bd3));

扩展:在金融领域,也可以使用一些第三方库,例如
<dependency><groupId>org.javamoney</groupId><artifactId>moneta</artifactId><version>1.1</version>
</dependency>
里面的Money类对金额做了显性的抽象,增加了金额的单位,避免了直接使用 BigDecimal 踩一些坑。
2.2、JavaScript 中解决计算精度丢失的问题
解决方法:decimal.js
decimal.js为 JavaScript 提供十进制类型的任意精度数值,是使用的二进制来计算的,所以能解决js的精度问题。
官网:http://mikemcl.github.io/decimal.js
GitHub地址:https://github.com/MikeMcl/decimal.js
用法如下:
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title></title></head><body><script src='js/decimal.js'></script><script type="text/javascript">var a=0.3*3;console.log(a);var b=0.1+0.2;console.log(b);//使用decimallet a1 = new Decimal(0.3).mul(new Decimal(3));console.log(a1.toNumber());let b2 = new Decimal(0.1).add(new Decimal(0.2));console.log(b2.toNumber());</script></body>
</html>
结果如下:

// 加法
let c = new Decimal(a).add(new Decimal(b));
// 减法
let d = new Decimal(a).sub(new Decimal(b));
// 乘法
let e = new Decimal(a).mul(new Decimal(b));
// 除法
let f = new Decimal(a).div(new Decimal(b));
3、使用建议
针对浮点数值存储和计算,大佬超给大家简单罗列了以下几点看法,欢迎补充。
-
禁止通过判断两个浮点数是否相等来控制某些业务流程:在比较浮点数时,由于存在误差,往往会出现意料之外的结果。
-
整型存储其最小单位的值:在要求绝对精确表示的业务场景下,比如金融行业的货币表示,展示时可以转换成该货币的常用单位,比如人民币使用分存储,美元使用美分存储。
-
数组保存小数部分的数据:在要求精确表示小数点位的业务场景下,比如圆周率要求存储小数点后 1000 位数字,使用单精度和双精度浮点数类型保存是难以做到的。
-
数据库中保存小数时,推荐使用 decimal 类型。
更多技术干货,请持续关注程序员大佬超。
原创不易,转载请注明出处。
相关文章:
浮点数值计算精度丢失问题剖析及解决方法
文章目录1、原因分析2、解决方法2.1、Java中使用 BigDecimal 类2.2、JavaScript 中解决计算精度丢失的问题3、使用建议1、原因分析 首先我们来看个反直觉的浮点数值计算 System.out.println(0.3*3);有的同学可能要问为啥不是0.9? 首先要知道为什么会产生这个问题…...
字符串匹配 - 模式预处理:朴素算法(Naive)(暴力破解)
朴素的字符串匹配算法又称为暴力匹配算法(Brute Force Algorithm),最为简单的字符串匹配算法。算法简介朴素的字符串匹配算法又称为暴力匹配算法(Brute Force Algorithm),它的主要特点是:没有预…...
CVE-2021-42278 CVE-2021-42287域内提权漏洞
漏洞介绍2021 年 11 月 9 日,国外研究员在推特上发布了AD相关的 CVE,CVE-2021-42278 & CVE-2021-42287 ,两个漏洞组合可导致域内普通用户权限提升至域管权限。CVE-2021-42278:是一个安全绕过漏洞,允许通过修改机器…...
关于IcmpSendEcho2的使用和回调问题
由于我的需求是短时间内ping多台机子,所以需要异步执行,微软提供的例子是同步方式的,根据微软官方提供的icmpSendEcho2 函数的信息 ,我需要定义一个空的宏PIO_APC_ROUTINE_DEFINED ,定义完之后,编译又出现…...
XQuery 术语
在 XQuery 中,有七种节点:元素、属性、文本、命名空间、处理指令、注释、以及文档节点(或称为根节点)。 XQuery 术语 节点 在 XQuery 中,有七种节点:元素、属性、文本、命名空间、处理指令、注释、以及文…...
会议论文分享-Security22-状态感知符号执行
Ferry: State-Aware Symbolic Execution for Exploring State-Dependent Program Paths1.引言2.问题陈述与分析2.1.实现状态感知符号执行的挑战2.2.真实程序的特征2.3.Ferry的模型2.3.1.程序状态的定义2.3.2.状态描述变量的特征3.Design3.1.Overview of Ferry3.2.状态描述变量识…...
吴恩达深度学习笔记(八)——卷积神经网络(上)
一、卷积相关 用一个ff的过滤器卷积一个nn的图像,假如padding为p,步幅为s,输出大小则为: [n2p−fs1][n2p−fs1][\frac{n2p-f}{s}1][\frac{n2p-f}{s}1][sn2p−f1][sn2p−f1] []表示向下取整(floor) 大部分深度学习…...
14 基数排序(桶排序)
文章目录1 基数排序基本思想2 基数排序的代码实现2.1 java2.2 scala3 基数排序总结1 基数排序基本思想 1) 基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort&#…...
汉明距离Java解法
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。 给你两个整数 x 和 y,计算并返回它们之间的汉明距离。 例: 输入:x 1, y 4 输出:2 解释: 1 (0 0 0 1) 4 (0 1 0 0) ↑ ↑ 上…...
Netty服务端请求接受过程源码剖析
目标 服务器启动后,客户端进行连接,服务器端此时要接受客户端请求,并且返回给客户端想要的请求,下面我们的目标就是分析Netty 服务器端启动后是怎么接受到客户端请求的。我们的代码依然与上一篇中用同一个demo, 用io.…...
金三银四春招特供|高质量面试攻略
🔰 全文字数 : 1万5千 🕒 阅读时长 : 20min 📋 关键词 : 求职规划、面试准备、面试技巧、谈薪职级 👉 公众号 : 大摩羯先生 本篇来聊聊一个老生常谈的话题————“面试”。利用近三周工作午休时间整理了这篇洋洋洒洒却饱含真诚…...
搭建Hexo博客-第4章-绑定自定义域名
搭建Hexo博客-第4章-绑定自定义域名 搭建Hexo博客-第4章-绑定自定义域名 搭建Hexo博客-第4章-绑定自定义域名 在这一篇文章中,我将会介绍如何给博客绑定你自己的域名。其实绑定域名本应该很简单的,但我当初在这上走了不少弯路,所以我觉得有…...
lightdb-sql拦截
文章目录LightDB - sql 审核拦截一 简介二 参数2.1 lightdb_sql_mode2.2 lt_firewall.lightdb_business_time三 规则介绍及使用3.1 select_without_where3.1.1 案例3.2 update_without_where/delete_without_where3.2.1 案例3.3 high_risk_ddl3.3.1 案例LightDB - sql 审核拦截…...
二进制中1的个数-剑指Offer-java位运算
一、题目描述编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 1 的个数(也被称为 汉明重量).)。提示:请注意,在某些语言(如 Java&…...
学自动化测试可以用这几个练手项目
练手项目的业务逻辑比较简单,只适合练手,不能代替真实项目。 学习自动化测试最难的是没有合适的项目练习。 测试本身既要讲究科学,又有艺术成分,单单学几个 api 的调用很难应付工作中具体的问题。 你得知道什么场景下需要添加显…...
2023年保健饮品行业分析:市场规模不断攀升,年度销额增长近140%
随着人们健康意识的不断增强,我国保健品市场需求持续增长,同时,保健饮品的市场规模也在不断攀升。 根据鲸参谋电商数据显示,2022年度,京东平台上保健饮品的年度销量超60万件,同比增长了约124%;该…...
2023-02-17 学习记录--TS-邂逅TS(一)
TS-邂逅TS(一) 不积跬步,无以至千里;不积小流,无以成江海。💪🏻 一、TypeScript在线编译器 https://www.typescriptlang.org/play/ 二、类型 1、普通类型 number(数值型ÿ…...
SpringMVC创建异步回调请求的4种方式
首先要明确一点,同步请求和异步请求对于客户端用户来讲是一样的,都是需客户端等待返回结果。不同之处在于请求到达服务器之后的处理方式,下面用两张图解释一下同步请求和异步请求在服务端处理方式的不同:同步请求异步请求两个流程…...
MySQL(二)表的操作
一、创建表 CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎; 说明: field 表示列名 datatype 表示列的类型 character set 字符集,如…...
SpringCloud - 入门
目录 服务架构演变 单体架构 分布式架构 分布式架构要考虑的问题 微服务 初步认识 案例Demo 服务拆分注意事项 服务拆分示例 服务调用 服务架构演变 单体架构 将业务的所有功能集中在一个项目中开发,打成一个包部署优点: 架构简单部署成本低缺…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
MySQL 部分重点知识篇
一、数据库对象 1. 主键 定义 :主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 :确保数据的完整性,便于数据的查询和管理。 示例 :在学生信息表中,学号可以作为主键ÿ…...
