ieee754浮点数转换工具_关于JS浮点数运算不精确的原因和解决方案

背景

之前在一个项目中,涉及到了金额,协议组定的标准是按照分的单位进行传递的,但是交互上,web页面中为了更友好的体验,是使用的元作为单位的,这个时候就需要转换一下单位

本来是很简单的一个转化的需求,在和后端联调的时候发现,保存的时候返回了参数错误,原因就是由于js浮点数精度带来的影响,导致保存的时候保存的位数特别多。

之前的开发过程中,对这个不精确的问题只是了解,有问题了就parseInt一下,但没有去细想过要怎么解决,所以今天整理了一下之后分享一下,先了解下原因,再看下怎么解决和规避

问题

输入金额 0.55,我传递之后应该乘 100 后下发,正常来说应该传的是55,但是实际上,由于精度丢失,最后的结果如下图所示:

cda396eaab1106b4d6af2a0c97df2d87.png

那追根溯源,到底为什么会产生精度丢失的问题呢?

计算机底层只有0 和 1, 所以所有的运算最后实际上都是二进制运算。十进制整数利用辗转相除的方法可以准确地转换为二进制数,但浮点数呢?

先看下面一张图,是关于IEEE 754标准(IEEE二进位浮点数算术标准(IEEE Standard for Floating-Point Arithmetic)的标准编号):

56594c10c6718d4d38df00532893267a.png

这个标准是JS的浮点数的实现标准,大概解释一下这张图就是:

  • 第一位是符号位
  • 中间11位代表的是指数位
  • 最后的52位代表尾数位

也就是说,浮点数最终在运算的时候实际上是一个符合该标准的二进制数

我们可以看一个例子:

d17946871a25eda02b3db09a334ecdd1.png

为了验证该例子,我们得先知道怎么将浮点数转换为二进制,整数我们可以用除2取余的方式,小数我们则可以用乘2取整的方式。

0.1转换为二进制:

  • 0.1 * 2,值为0.2,小数部分0.2,整数部分0
  • 0.2 * 2,值为0.4,小数部分0.4,整数部分0
  • 0.4 * 2,值为0.8,小数部分0.8,整数部分0
  • 0.8 * 2,值为1.6,小数部分0.6,整数部分1
  • 0.6 * 2,值为1.2,小数部分0.2,整数部分1
  • 0.2 * 2,值为0.4,小数部分0.4,整数部分0
  • 从0.2开始循环

0.2转换为二进制:

  • 可以直接参考上述,肯定最后也是一个循环的情况

所以最终我们能得到两个循环的二进制数:

0.1:0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1100 ...

0.2:0.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 ...

这两个的和的二进制就是:

sum:0.0100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 1100 ...

所以最终我们只能得到和的近似值(按照IEEE 754标准保留 52位,按 0舍1入 来取值)

利用按权相加法,sum的十进制数则为:

sum ≈ 0.30000000000000004

这个例子说明了什么?

说明JS浮点数精度的缺失实际上是因为浮点数的小数部分无法用二进制很精准的转换出来,而以近似值来进行运算的话,肯定就存在精度的问题

解决

了解了真相之后,我们可以怎么处理呢?

我的项目中是直接parseInt了一下,因为误差很小,可以忽略不计。

但后来想想不能这么就放过去了,这种一刀切的方法太暴力了,万一涉及到需要特别精确的场景,这种方法会有问题。

所以,最好的办法还是需要找方法提高精度,直接规避。

思路其实非常简单,既然浮点数的情况下会丢失精度,那我们所有运算的时候都先小浮点数转换为整数,等计算完之后,再按比例转换会浮点数,这样就避免了再二进制十进制转换的时候计算机的精度问题。

比如上面的加法的例子,我们定义一个工具函数add:

const 

现在我们再来看看结果:

18c0a6e7d56b7b4c59cba561dc8dfa56.png

但有个问题,我们之前还有个例子,0.55在乘的时候就会产生精度问题,所以这里我们再优化一下,把一个数变成整数,是不是也可以理解为将其变成字符串后把小数点去掉再转回数字,这样效果和相乘的效果不是一样的吗:

const 

这样就解决了。

当然,这只是考虑了最简单的情况,比如如果位数不同还要特殊处理下。不过只要思路有了,后续的就属于添砖加瓦了,考虑得很全面的话,可以考虑封装成一个库,这篇文章就权当抛砖引玉吧。

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

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

相关文章

leetcode 6 --- convertZ

1 题目 将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。 2 解法 2.1 罗列每一行的情况 2.1.1 思路 按照原序列的顺序进行遍历, 看每个元素按照Z字型排列的话会处于哪一行, 最后合并所有行. 2.1.2 代码 string convert(str…

fir滤波器matlab实现_关于FIRamp;IIR系统的算法说明以及结果验证(1)

首先,做一个简短的开场白。本贴主要是一个关于信号处理方面的学习笔记。主要目的有三,作为研究笔记留存分享我个人的理解与专业人士进行意见交换另外由于楼主不是教课员,因此无法保证算法分析以及个人理解的完全正确性,若本人对知…

操作系统中的全局页面置换算法

1 全局页面置换算法 以上页面置换算法都是针对单一的应用程序的页面置换算法, 且有一个前提, 就是给单一应用程序分配的物理页帧数量是一定的. 现实中, 给一个应用程序分配的物理页帧数, 该程序产生的缺页中断也就越少, 而且程序运行过程中, 可能某些阶段对于内存的读写操作很…

怎么更新opengl.dll文件_安装累积更新丢文件似乎已成为惯例 KB4556799同样出现文件丢失问题...

从 Windows 10 Version 1903 版发布开始就经常出现用户升级版本或者安装累积更新导致个人文件丢失的问题。但是以前这种问题似乎并不算频繁,然而从今年开始微软发布的累积更新几乎都存在这样的问题让很多用户苦恼。而微软至今从未承认过安装累积更新会丢失用户的个人…

非全局页面置换算法

1 先进先出算法(FIFO) 1.1 基本思路: 选择在内存中驻留时间最长的页面并淘汰之. 具体来说, 系统维护着一个链表, 记录了所有位于内存当中的逻辑页面. 从链表的排列顺序来看, 链首页面的驻留时间最长, 链尾页面的驻留时间最短. 当发生一个缺页中断时, 把链首页面的淘汰出局, 并…

github打开前端样式丢失_工具资源系列之 github 上各式各样的小徽章从何而来?...

前言平时大家在在逛 github 时或多或少都看到过项目首页各式各样的小徽章,不知道你是否和我一样好奇这些小徽章都是哪来的呢?首先我们先来一睹为快目前前端开发的三大主流框架: var ,看一看他们的 github 项目首页有哪些小徽章吧!小结:前端三大框架的徽章均不相同,由此可见,这…

操作系统进程管理

1 进程概述 进程: 一个具有一定的功能的程序在一个数据集合上的一次动态执行过程. 1.1 进程组成 一个进程应该包括: 程序的代码程序处理的数据程序计数器中的值, 指使下一条将运行的指令一组通用的寄存器的当前值, 堆, 栈一组系统资源(如打开的文件) 总之, 进程包含了正在运…

vs code ipynb文件_UE4引擎 源码的获取、安装,以及VS配置

1.首先我们需要注册一个Epic账户,网址如下http://api.unrealengine.com/CHN/GettingStarted/Installation/index.html#bookmark12.创建GitHub账户https://github.com/3.登录UE4社区点击个人进入到个人之后点击连接的账户,之后在下面填写我们GITHUB的昵称…

js bind 传参、_js中的面向对象(一)

面向对象要解决的问题提到面向对象,大家的第一反应就是封装、继承和多态。对其做如下解释:封装:影藏细节(A对A——将多行代码取个名字或A对B——API调用合作)继承:继承的意思就是同上跟上述一样&#xff0c…

计算机网络中的物理层

1 基础概念 物理层解决如何在链接各种计算机的传输媒体上传输数据比特流, 而不是指具体的传输媒体. 物理层的主要任务, 确定与传输媒体接口有关的一些特性. -> 定义标准 1.1 物理层规定的特性: 机械特性: 定义物理连接的特性, 规定物理连接时所采用的规格, 接口形状, 引…

二元置信椭圆r语言_医学统计与R语言:多分类logistic回归HosmerLemeshow拟合优度检验...

微信公众号:医学统计与R语言如果你觉得对你有帮助,欢迎转发输入1:multinominal logistic regression "nnet")结果1: test (multinomial model)输入2:ordinal logistic regression "MASS")结果2&am…

python3.7.2安装与配置_python3.7.0 安装与配置

python 3.7.0 X64下载地址: https://www.python.org/ftp/python/3.7.0/python-3.7.0-amd64.exe 更多版本下载请移步到:https://www.python.org/downloads/release/python-370/ python可以在同一台机器上进行多版本的安装使用。 安装好python3.7.0后确认系…

python3设置编码_python3 中文乱码与默认编码格式设定方法

python默认编码格式是utf-8。在python2.7中,可以通过sys.setdefaultencoding(gbk)设定默认编码格式,而在python3.3中sys.setdefaultencoding()这个函数已经没有了。在python3.3中该如何设置内置的默认编码格式啊!急求!&#xff01…

操作系统处理器调度

1 背景 1.1 上下文切换: 切换CPU的当前任务, 从一个进程/线程到另一个保存当前进程/线程在PCB/TCB中的执行上下文(CPU状态)读取下一个进程/线程的上下文 1.2 CPU调度: 从就绪队列中挑选一个进程/线程作为CPU将要运行的下一个进程/线程调度程序: 挑选进程/线程的内核函数(通过…

c# 操作redisclient 设置过期时间_C# Redis分布式锁单节点

(给DotNet加星标,提升.Net技能)转自:热敷哥cnblogs.com/refuge/p/13774008.html为什么要用分布式锁?先上一张截图,这是在浏览别人的博客时看到的.在了解为什么要用分布式锁之前,我们应该知道到底什么是分布式锁.锁按照不同的维度,有多种分类.比如1、悲观…

计算机网络数据链路层检错编码 --- 循环冗余码CRC

实例说明 假如要发送的数据是1101 0110 11, 采用CRC校验, 生成多项式是10011, 那么最终发送的数据应该是? 发送端发送过程: 1. 最终发送的数据 要发送的数据 帧检验序列FCS(冗余码) 2. 利用生成多项式计算冗余码 计算冗余码的方法: 1. 加0, 要根据生成多项式中的阶为, 则…

python和arduino串口通信_利用串行通信实现python与arduino的同步

我有一个需要:使用arduino将伺服电机移动到某个位置并在该位置停止 让一个由python控制的相机在那个位置获取图像 当图像被采集到时,伺服机构应该移动到一个对称的位置 这个序列重复N次 所以我尝试使用串行通信同步arduino和python代码。在arduino端&…

计算机网络中数据链路层编码纠错编码 --- 海明码

1 概述 首先, 海明码是计算机网络中数据链路层的针对帧的位错提出的一种纠错编码方式. 海明码可以发现双比特错, 但纠正单比特错. 工作原理(简单解释): 牵一发动全身 2 工作流程 2.1 确定校验码位数r 海明不等式: 其中为冗余信息位数, 为信息位数 如果给定要发送的数据, …

python在教育领域的应用_浅谈Python的主要应用领域

Python的用途较为广泛,小编也会经常接触到各种与Python有关的项目,也算是一名忠实的开发者。能够遇到关于Python用途的问题,也很乐意回答。Python这个概念非常大,它的定位是“计算机程序设计语言”,从它的特点来看&…

计算机操作系统同步互斥

1 背景 在计算机系统里面, 多道程序设计是现代操作系统的重要特征, 且并行起到了很大的作用, 所以操作系统抽象出来了线程/进程的概念用来支持多道程序设计, 同时, 各个进程之间需要进行交互, CPU也需要进行调度来支持多进程. 多进程会涉及到共享资源访问的问题, 如果操作系统…