前言
在使用JavaScript计算浮点数时,会出现精度丢失的问题,其实不止是JavaScript,只要是遵循IEEE 754标准的语言都存在这个问题
为什么会精度丢失?
-
表示范围:
浮点数使用二进制表示,某些十进制数无法精确表示。例如,0.1 和 0.2 在二进制中是无限循环小数,因此只能近似表示。 -
精度限制:
浮点数在计算机中是以固定的位数存储的。对于双精度浮点数(如 JavaScript 中的Number类型),它通常使用64位,其中包括符号位1位、指数位11位和尾数位53位。这种限制导致了某些运算的结果无法精确表示。 -
累积误差:
在进行多次浮点运算时,微小的误差会逐渐累积,导致最终结果远离预期值。
0.1 + 0.2 === 0.3?
答案是错误的
const a = 0.1;
const b = 0.2;
console.log(a + b); // 0.30000000000000004
这就是由于精度丢失的问题
其中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;
- 返回结果:将总和除以因子,转换回浮点数,并返回结果。