返回

浮点数真面目:计算机如何处理0.1+0.2≠0.3?

前端

计算机如何存储浮点数?

计算机以二进制形式存储数据,而浮点数则是使用二进制指数来表示实数。为了在有限的二进制位中表示尽可能多的实数,浮点数采用了科学计数法,即:

数字 = 系数 * 底数^指数

在计算机中,底数固定为2,指数以二进制表示,而系数则以二进制小数表示。这种表示方式称为IEEE 754标准,也是JavaScript中浮点数的存储方式。

IEEE 754标准

IEEE 754标准定义了两种最常用的浮点数格式:单精度浮点数和双精度浮点数。单精度浮点数占用32位,而双精度浮点数占用64位。

单精度浮点数的结构如下:

符号位(1位):表示数字的正负。
指数位(8位):表示指数。
尾数位(23位):表示系数。

双精度浮点数的结构与单精度浮点数类似,但指数位和尾数位分别为11位和52位。

二进制

二进制是计算机使用的计数系统,只有0和1两个数字。二进制数的位权从右到左依次为1、2、4、8、16、32、64、128、256,以此类推。

舍入误差

由于计算机以有限的二进制位存储浮点数,因此在存储过程中可能会产生舍入误差。舍入误差是指实际值与存储值之间的差。

舍入方式

有四种常见的舍入方式:

  1. 截尾舍入:直接舍去尾数位后面的所有位。
  2. 四舍五入:如果尾数位后面的第一位是5,则尾数位进1,否则尾数位不变。
  3. 最近偶数舍入:如果尾数位后面的第一位是5,则尾数位进1,如果尾数位后面的第一位是偶数,则尾数位不变,如果尾数位后面的第一位是奇数,则尾数位减1。
  4. 向上舍入:始终将尾数位进1。

JavaScript中的浮点数

JavaScript中使用双精度浮点数表示数字。双精度浮点数占用64位,其中1位表示符号位,11位表示指数位,52位表示尾数位。

0.1+0.2≠0.3的原因

0.1和0.2都是有限小数,但在二进制中它们都是无限小数。因此,计算机在存储0.1和0.2时,会产生舍入误差。由于舍入方式的不同,舍入误差也不同。

在JavaScript中,默认的舍入方式是最近偶数舍入。因此,0.1和0.2在存储时都会产生舍入误差。这些舍入误差会导致0.1+0.2不等于0.3。

避免舍入误差

为了避免舍入误差,可以在JavaScript中使用BigDecimal类。BigDecimal类使用字符串来表示数字,因此可以避免舍入误差。但是,BigDecimal类的运算速度比原生浮点数慢,因此在使用时需要权衡利弊。

结论

0.1+0.2不等于0.3的原因在于计算机以二进制形式存储浮点数,而在二进制中0.1和0.2都是无限小数。因此,计算机在存储0.1和0.2时,会产生舍入误差。由于舍入方式的不同,舍入误差也不同。在JavaScript中,默认的舍入方式是最近偶数舍入。因此,0.1和0.2在存储时都会产生舍入误差。这些舍入误差会导致0.1+0.2不等于0.3。为了避免舍入误差,可以在JavaScript中使用BigDecimal类。