Skip to content

JavaScript精度

1 背景

主要是在一次面试的过程中,对方问我 js 精度的问题,我没有回答上来,平时只留意解决精度不准确的问题(1.放大计算,计算好了再缩小; 2.使用计算库;3. 交给后端计算等),一时半会还真没有想到这个问题是怎么造成的,现在得空,就问 GPT,帮我解决心中的疑惑

在这篇博客中,我们将详细解释为什么会出现这个问题,并介绍一些解决这个问题的方法。

2 前言

Javascript是一种强大的编程语言,可以轻松处理各种数据类型,包括字符串、数字、数组等等。然而,在处理数字问题时,我们有可能遇到小数相加不准确的问题。

这个问题一直存在于Javascript语言中,它的原因是因为Javascript采用的是二进制浮点数表示法

在这篇博客中,我们将详细解释为什么会出现这个问题,并介绍一些解决这个问题的方法。

3 Javascript中小数相加的问题

在Javascript中,小数就是浮点数。在进行小数相加时,我们可能会遇到下面这种情况:

javascript
var a = 0.1;
var b = 0.2;
var c = a + b;
console.log(c); // 输出0.300000004

这个结果看起来很奇怪,因为我们期望的输出应该是0.3,而不是0.300000004。这是因为在Javascript中,小数的计算是采用二进制浮点数表示法进行的。在这种表示法中,一个小数通常是由一个正负符号、一个基数(也称作底数)和一个指数(也称作阶码)组成的

3.1 二进制无法准确地表示某些十进制小数

1. 十进制小数在二进制中的表示需要无限位,但计算机只能表示有限位数。
2. 比如在十进制中,0.1这个数可以精确地表示,但是转换到二进制后就是0.0001100110011001...(无限循环)。
3. 这种无法精确表示的小数在计算机中会产生舍入误差。例如0.1 + 0.2在计算机中不等于0.3,而是0.30000000000000004。
这种舍入误差在一些对精度要求很高的应用中会产生问题,比如金融计算、科学计算等。

3.2 限位是什么意思

1. 计算机使用二进制表示数字,每一位被称为一个"bit"。
2. 计算机能表示的数字范围取决于使用的bit数。比如8bit可以表示0到255之间的整数。
3. 对于小数,计算机通常会使用固定的小数位数来表示,比如float类型通常使用32bit,可以表示小数点后7位左右。
4. 这就造成了"限位"的问题 - 无法用有限的bit数精确表示所有的实数。
5. 对于无法精确表示的小数,计算机会进行舍入操作,从而产生微小的误差。这在需要高精度计算的场景下会带来问题

3.3 为什么 十进制中,0.1这个数可以精确地表示,但是转换到二进制后就是0.0001100110011001...(无限循环)

1. 十进制中的0.1可以精确地表示,是因为它是一个"基本"分数,即10的负一次方。在十进制系统中,小数点后的每一位都代表10的负一次方、负二次方等。
2. 而在二进制系统中,小数点后的每一位代表2的负一次方、负二次方等。
3. 0.1这个数在十进制中等于1/10,但在二进制中无法用有限位数精确表示。
4. 将0.1转换成二进制,需要反复除以2并记录余数。此过程会产生无限循环小数0.0001100110011001...。
5. 这是因为在二进制系统中,1/10这个分数无法用有限位二进制数精确表示。它是一个无限循环小数。
6. 计算机只能用有限位存储数字,因此无法完全精确地表示0.1这样的数字,只能近似表示。这就是导致舍入误差的根本原因。

3.4 将0.1转换成二进制,需要反复除以2并记录余数 具体过程

步骤如下:

取小数部分0.1
将0.1乘以2, 得到 0.2, 记录整数部分0
将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
重复步骤4-7, 会发现余数不断重复, 形成无限循环小数
最终0.1的二进制表示为:0.0001100110011001100110011001100110011...

计算的时候,是先将小数转换为二进制,有些小数转换为二进制是无限循环小数,计算的时候,就会导致计算不准。

Released under the MIT License.