JavaScript中的浮点数计算

350 阅读3分钟

前言

在使用JavaScript计算浮点数时,会出现精度丢失的问题,其实不止是JavaScript,只要是遵循IEEE 754标准的语言都存在这个问题

为什么会精度丢失?

  1. 表示范围
    浮点数使用二进制表示,某些十进制数无法精确表示。例如,0.1 和 0.2 在二进制中是无限循环小数,因此只能近似表示。

  2. 精度限制
    浮点数在计算机中是以固定的位数存储的。对于双精度浮点数(如 JavaScript 中的 Number 类型),它通常使用64位,其中包括符号位1位、指数位11位和尾数位53位。这种限制导致了某些运算的结果无法精确表示。

  3. 累积误差
    在进行多次浮点运算时,微小的误差会逐渐累积,导致最终结果远离预期值。

0.1 + 0.2 === 0.3?

答案是错误的

const a = 0.1;
const b = 0.2;
console.log(a + b); // 0.30000000000000004

image.png

这就是由于精度丢失的问题

其中0.1转换为二进制的过程为

将 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
  • ...

如此循环下去,0.1 在二进制中表现为 0.00011001100110011...,这是一个无限循环的小数。

0.2同理,所以他们相加得到的二进制数也是个无限循环的小数,这也就是造成进度丢失的根本原因

解决方式

  • 将其变为整数再相加
function add(num1, num2) {
    // 将输入的数字转换为字符串,找到小数位数
    const str1 = num1.toString();
    const str2 = num2.toString();
    
    const decimalPlaces1 = str1.includes('.') ? str1.split('.')[1].length : 0;
    const decimalPlaces2 = str2.includes('.') ? str2.split('.')[1].length : 0;
    
    // 计算最大的位数
    const maxDecimalPlaces = Math.max(decimalPlaces1, decimalPlaces2);
    
    // 将数字转换为整数
    const factor = Math.pow(10, maxDecimalPlaces);
    const intNum1 = Math.round(num1 * factor);
    const intNum2 = Math.round(num2 * factor);
    
    // 进行加法运算
    const sum = intNum1 + intNum2;
    
    // 将结果转换回浮点数
    return sum / factor;
}

// 示例使用
console.log(add(0.1, 0.2)); // 输出: 0.3
console.log(add(0.1, 0.3)); // 输出: 0.4
console.log(add(1.5, 2.3)); // 输出: 3.8
function add(num1, num2) {
  • 函数定义:定义一个名为 add 的函数,接受两个参数 num1 和 num2,它们都是需要相加的数字。
const str1 = num1.toString();
const str2 = num2.toString();
  • 转换为字符串:将 num1 和 num2 转换为字符串格式,以便后续处理小数位数。
const decimalPlaces1 = str1.includes('.') ? str1.split('.')[1].length : 0;
const decimalPlaces2 = str2.includes('.') ? str2.split('.')[1].length : 0;
  • 计算小数位数

    • 使用 includes 方法检查字符串中是否包含小数点 .
    • 如果包含小数点,使用 split 方法将字符串分割成两部分,并获取小数部分的长度。
    • 如果不包含小数点,则小数位数为 0。
const maxDecimalPlaces = Math.max(decimalPlaces1, decimalPlaces2);
  • 确定最大小数位数:使用 Math.max 函数找到两个数字中小数位数的最大值,以便统一处理。
const factor = Math.pow(10, maxDecimalPlaces);
  • 计算因子:计算 10 的最大小数位数的幂,目的是将浮点数转换为整数。例如,如果最大小数位数是 2,则因子为 100。
const intNum1 = Math.round(num1 * factor);
const intNum2 = Math.round(num2 * factor);
  • 转换为整数

    • 将 num1 和 num2 乘以因子,得到相应的整数。
    • 使用 Math.round 方法确保在转换过程中不会产生四舍五入的误差。
const sum = intNum1 + intNum2;
  • 进行加法运算:将两个整数相加,得到 sum
return sum / factor;
  • 返回结果:将总和除以因子,转换回浮点数,并返回结果。