javascript基础二十七:说说 JavaScript 数字精度丢失的问题,解决方案?

news2024/11/27 17:23:33

在这里插入图片描述
一、场景复现
一个经典的面试题

0.1 + 0.2 === 0.3 // false

为什么是false呢?

先看下面这个比喻

比如一个数 1÷3=0.33333333…

这是一个除不尽的运算,3会一直无限循环,数学可以表示,但是计算机要存储,方便下次再使用,但0.333333… 这个数无限循环,再大的内存它也存不下,所以不能存储一个相对于数学来说的值,只能存储一个近似值,这么存储后再取出时自然就出现精度丢失问题

二、浮点数

“浮点数”是一种表示数字的标准,整数也可以用浮点数的格式来存储

我们也可以理解成,浮点数就是小数

在JavaScript中,现在主流的数值类型是Number,而Number采用的是IEEE754规范中64位双精度浮点数编码

这样的存储结构优点是可以归一化处理整数和小数,节省存储空间

对于一个整数,可以很轻易转化成十进制或者二进制。但是对于一个浮点数来说,因为小数点的存在,小数点的位置不是固定的。解决思路就是使用科学计数法,这样小数点位置就固定了

而计算机只能用二进制(0或1)表示,二进制转换为科学记数法的公式如下:

在这里插入图片描述
其中,a的值为0或者1,e为小数点移动的位置

举个粟子:
27.0转化成二进制为11011.0 ,科学计数法表示为:

在这里插入图片描述

前面讲到,javaScript存储方式是双精度浮点数,其长度为8个字节,即64位比特

64位比特又可分为三个部分:

  • 符号位S:第 1 位是正负数符号位(sign),0代表正数,1代表负数
  • 指数位E:中间的 11 位存储指数(exponent),用来表示次方数,可以为正负数。在双精度浮点数中,指数的固定偏移量为1023
  • 尾数位M:最后的 52 位是尾数(mantissa),超出的部分自动进一舍零

如下图:
在这里插入图片描述
二、问题分析

再回到问题上

0.1 + 0.2 === 0.3 // false

通过上面的学习,我们知道,在javascript语言中,0.1 和 0.2 都转化成二进制后再进行运算

// 0.1 和 0.2 都转化成二进制后再进行运算
0.00011001100110011001100110011001100110011001100110011010 +
0.0011001100110011001100110011001100110011001100110011010 =
0.0100110011001100110011001100110011001100110011001100111

// 转成十进制正好是 0.30000000000000004

所以输出false

再来一个问题,那么为什么x=0.1得到0.1?

主要是存储二进制时小数点的偏移量最大为52位,最多可以表达的位数是2^53=9007199254740992,对应科学计数尾数是 9.007199254740992,这也是 JS 最多能表示的精度

它的长度是 16,所以可以使用 toPrecision(16) 来做精度运算,超过的精度会自动做凑整处理

.10000000000000000555.toPrecision(16)
// 返回 0.1000000000000000,去掉末尾的零后正好为 0.1

但看到的 0.1 实际上并不是 0.1。不信你可用更高的精度试试:

0.1.toPrecision(21) = 0.100000000000000005551

如果整数大于 9007199254740992 会出现什么情况呢?

由于指数位最大值是1023,所以最大可以表示的整数是 2^1024 - 1,这就是能表示的最大整数。但你并不能这样计算这个数字,因为从 2^1024 开始就变成了 Infinity

> Math.pow(2, 1023)
8.98846567431158e+307

> Math.pow(2, 1024)
Infinity

那么对于 (2^53, 2^63) 之间的数会出现什么情况呢?

  • (2^53, 2^54) 之间的数会两个选一个,只能精确表示偶数
  • (2^54, 2^55) 之间的数会四个选一个,只能精确表示4个倍数
  • … 依次跳过更多2的倍数

要想解决大数的问题你可以引用第三方库 bignumber.js,原理是把所有数字当作字符串,重新实现了计算逻辑,缺点是性能比原生差很多

小结

计算机存储双精度浮点数需要先把十进制数转换为二进制的科学记数法的形式,然后计算机以自己的规则{符号位+(指数位+指数偏移量的二进制)+小数部分}存储二进制的科学记数法

因为存储时有位数限制(64位),并且某些十进制的浮点数在转换为二进制数时会出现无限循环,会造成二进制的舍入操作(0舍1入),当再转换为十进制时就造成了计算误差

三、解决方案

理论上用有限的空间来存储无限的小数是不可能保证精确的,但我们可以处理一下得到我们期望的结果

当你拿到 1.4000000000000001 这样的数据要展示时,建议使用 toPrecision 凑整并 parseFloat 转成数字后再显示,如下:

parseFloat(1.4000000000000001.toPrecision(12)) === 1.4 // True

封装成方法就是:

function strip(num, precision = 12) {
return +parseFloat(num.toPrecision(precision));
}

对于运算类操作,如 ±*/,就不能使用 toPrecision 了。正确的做法是把小数转成整数后再运算。以加法为例:

/**
 * 精确加法
 */
function add(num1, num2) {
  const num1Digits = (num1.toString().split('.')[1] || '').length;
  const num2Digits = (num2.toString().split('.')[1] || '').length;
  const baseNum = Math.pow(10, Math.max(num1Digits, num2Digits));
  return (num1 * baseNum + num2 * baseNum) / baseNum;
}

最后还可以使用第三方库,如Math.js、BigDecimal.js

也可以使用以下的方法

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

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

相关文章

IMX6ULL裸机篇之I2C实验-硬件原理图

一. I2C 实验简介 I2C实验,我们就来学习如何使用 I.MX6U 的 I2C 接口来驱动 AP3216C,读取 AP3216C 的传感器数据。 AP3216C是一个三合一的环境光传感器,ALSPSIRLED,ALS是环境光,PS是接近传感器,IR是红外L…

2023 华为 Datacom-HCIE 真题题库 12/12(完结)--含解析

单项选择题 1.[试题编号:190728] (单选题)以下哪种工具不能用来匹配BGP路由条目? A、基本ACL B、高级ACL C、IP PREFIX LIST D、Community Filter 答案:B 解析:高级ACL是一种用于过滤IPv4报文的ACL&#…

多层级table联动

elementui 多层级table联动: 引用: https://blog.csdn.net/weixin_44780971/article/details/130054925 https://blog.csdn.net/qq_42581563/article/details/114325920 需要了解的属性: select-all 全选的时候执行select : 选择…

MySQL 连接查询

文章目录 一,等值连接二,表别名三,多表等值连接四,自然连接五,自连接六,非等值内连接七,外连接(一)左外连接(二)右外连接(三&#xff…

Cookie与Session的工作流程

文章目录 Cookiecookie的工作流程1.cookie从哪里来2.cookie到哪里去3.cookie是做什么的 SessionSession工作流程 Cookie与Session都是http协议中的机制,都是用来追踪浏览器用户身份的会话方式.但是又有各自的工作流程. Cookie cookie是浏览器在本地存储数据的一种机制。 cookie…

java从入门到起飞——基础概念

目录 背景注释和关键字注释关键字 常量变量数据类型计算存储单元数据类型分类 标识符小驼峰命名法(方法、变量)大驼峰命名法(类) 类型转换自动类型转换强制类型转换 计算机中的数据存储总结 背景 学编程这么长时间了,重…

Java Swing花样玩法:教你用代码制作六一儿童节的精美贺卡(简单版)

✨博主:命运之光 ✨专栏:Java经典程序设计 前言:这篇博客在打开可能会自动播放视频,视频有音乐,请及时静音哈🙂 目录 ✨前言 ✨引言 ✨简单介绍一下Javaswing这项技术简单介绍一下Javaswing这项技术&a…

电子模块|压力传感器模块HX711---硬件介绍与C51STM32驱动

电子模块|压力传感器模块HX711---硬件介绍与C51&&STM32驱动 实物照片模块简介模块特点 硬件模拟输入供电电源时钟选择串口通讯复位和断电HX711相关部分的 PCB 设计 软件驱动C51软件代码STM32软件代码 实物照片 模块简介 HX711是一款专为高精度称重传感器而设计的24位A…

全志V3S嵌入式驱动开发(音频输出和音频录制)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 之前在芯片公司的时候,基本没有看过音频这一块,只知道有个alsa框架这么个知识点。要驱动音频,需要两部分&#…

10-风险管理:如何应对暗礁风险?系统化风险管理让你安心!

项目已到中期,目前看很顺利,但隐隐不安:项目进展越平稳,我越觉不安。我担心项目会不会存在什么风险,而自己却没发现。 这种担心很必要,因为项目从构思起,就存在风险。光担心没用,项…

如何用LoadRunner 做性能测试?一篇文章教会你

目录 一、loadrunner介绍 二、测试计划 三、创建测试脚本 四、创建测试场景 五、分析结果 六、性能指标 一、loadrunner介绍 loadrunner有三个软件,其中Virtual User Generator是用于录制测试脚本的,是一个虚拟用户生成器。Controller用于创建、运…

CSS常用属性

目录 1.CSS是什么? 2.基本语法 3.引入方式 1.内部样式表 2.行内样式表 3.外部样式 4.基础选择器 1.标签选择器 2.类选择器 3.id选择器 4.通配符选择器 基础选择器总结 5.复合选择器 1.后代选择器 2.子选择器 3.并集选择器 4.伪类选择器 1) 链接伪类…

行业风向:国产新能源汽车如何“扬帆起航”闯世界?

历经十余年的积累和发展,受益于国家财政政策的大力支持、行业技术水平的大幅提升、车企研发与营销费用的大力投入等多重因素,我国新能源汽车走向了高速发展阶段,并一举成为全球最大的新能源汽车市场,在续航里程、环境适应性、整车…

Hooks

私人博客 许小墨のBlog —— 菜鸡博客直通车 系列文章完整版,配图更多,CSDN博文图片需要手动上传,因此文章配图较少,看不懂的可以去菜鸡博客参考一下配图! 系列文章目录 前端系列文章——传送门 后端系列文章——传送…

以太网交换机的生成树协议STP

以太网交换机的生成树协议STP 笔记来源: 湖科大教书匠:以太网交换机的生成树协议STP 声明:该学习笔记来自湖科大教书匠,笔记仅做学习参考 如下图所示以太网中出现链路故障导致部分主机间无法进行通信 如何提高以太网的可靠性&am…

Elasticsearch:复制 - replication

在本篇文章中,我们来讲述 Elasrticsearch 集群中重要的一个概念 replication,也即复制。 了解 Elasticsearch 中的分片复制 默认情况下,索引由单个分片组成,但是如果存储分片的节点出现故障(例如磁盘故障)…

【Python基础】- 基础数据类型(下)

🤵‍♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞&#x1f4…

一文吃透Java并发高频面试题

内容摘自我的学习网站:topjavaer.cn 分享50道Java并发高频面试题。 线程池 线程池:一个管理线程的池子。 为什么平时都是使用线程池创建线程,直接new一个线程不好吗? 嗯,手动创建线程有两个缺点 不受控风险频繁创…

typescript 相关概念

TypeScript 开发环境搭建 下载Node.js 14.15.1版本64位:https://nodejs.org/dist/v14.15.1/node-v14.15.1-x64.msi node官网: Node.js 安装Node.js 使用npm全局安装typescript 进入命令行 输入: npm i -g typescript 创建一个ts文件 使用tsc对ts文件…

从0-1一起学习live555设计思想之一 基础运行环境 + 任务调度

系列文章目录 文章目录 系列文章目录前言一、基础组件总览二、UsageEnvironment三、BasicUsageEnvironment0四、BasicUsageEnvironment五、TaskScheduler六、BasicTaskScheduler0七、DelayQueue八、BasicTaskScheduler九、基础调度总结总结前言 一、基础组件总览 本篇开始分析…