为什么 0.1 + 0.1 !== 0.2

文章讨论了浮点数运算中的精度问题,解释了0.1+0.1不等于0.2的原因,并通过JavaScript中的map和parseInt函数实例展示了相关内容。同时分析了严格模式下函数作用域的变化以及变量声明的影响。

总结了几个很有意思的基础题目,分享一下。

为什么 0.1 + 0.1 !== 0.2

看到这个问题,不得不想到计算机中的数据类型,其中浮点数表示有限的精度。那么它就无法精确的表示所有的十进制小数,所以在在某些情况下,浮点数的运算可能会导致微小的精度误差。这就是为什么 0.1 + 0.1 不等于 0.2 的原因。

具体来说,0.10.2 这两个数字都无法在二进制浮点数表示中精确地表示为有限位数的小数。这导致了一个微小的误差,当它们相加时,这个误差会叠加,使得结果不等于 0.2

这个问题不仅适用于 JavaScript,还适用于大多数编程语言中使用浮点数表示的情况。为了处理这种情况,通常需要在比较浮点数时使用某种容忍误差的方法,而不是直接进行相等性比较。

在 JavaScript 中,可以使用以下方法来处理浮点数的比较:

// 使用小数点后的位数来比较
const num1 = 0.1 + 0.1;
const num2 = 0.2;
const tolerance = 0.000001; // 定义一个容忍误差

if (Math.abs(num1 - num2) < tolerance) {
  console.log("它们大致相等");
} else {
  console.log("它们不相等");
}

[“1”,“2”,“3”].map(parseInt) 结果是什么

第一感觉是不是打印[“1”, “2”, “3”]。不认真看确实会有这样的结论。实际上并不是。
这个题目涉及到了map和parseIent两个知识点,首先了解一下这两个函数。

  1. map:JavaScript 中的 map() 函数是数组对象的一个高阶函数,它用于对数组的每个元素执行一个指定的函数,并返回一个新的数组,新数组中的元素是原始数组元素按照指定函数进行转换后的结果。map() 函数的基本语法如下:
array.map(callback(currentValue[, index[, array]])[, thisArg])

参数说明:

  • callback:一个函数,用于对每个元素进行处理。这个函数接受三个参数:
    • currentValue:当前正在处理的数组元素。
    • index(可选):当前元素的索引。
    • array(可选):调用 map() 的数组本身。
  • thisArg(可选):可选参数,用于指定 callback 函数中 this 的值。

map() 函数的工作流程如下:

  • 从数组的第一个元素开始,依次对每个元素执行 callback 函数。
  • callback 函数的返回值存储在一个新数组中。
  • 返回新数组作为 map() 函数的结果。
  1. parseInt:函数接受两个参数:要解析的字符串和基数(进制)。但在 map 中,parseInt 被传递了三个参数:当前元素、当前索引和数组本身。

所以[“1”, “2”, “3”].map(parseInt)的结果是[1, NaN, NaN]。

map 将按照以下方式调用 parseInt

  • 第一次调用:parseInt("1", 0, ["1", "2", "3"]),其中基数为 0(表示自动识别进制)。
  • 第二次调用:parseInt("2", 1, ["1", "2", "3"]),其中基数为 1。
  • 第三次调用:parseInt("3", 2, ["1", "2", "3"]),其中基数为 2。

parseInt 在解析字符串时,如果第一个字符无法转换为数字,则返回 NaN。在这里,第一次调用 parseInt 时,字符串 “1” 可以成功解析为数字 1。然而,在第二次和第三次调用时,基数参数不符合期望的进制,因此解析失败,返回 NaN

因此,["1", "2", "3"].map(parseInt) 的结果是 [1, NaN, NaN],这是由于 parseInt 函数在不同基数下的解析失败导致的。

下列题目输出结果

(function a () {
  "use strict";
  var a = b = 3;
})();
console.log("输出a" + (typeof a !== 'undefined'));
console.log("输出b" + (typeof b !== 'undefined'));

非严格模式下运行
代码块 (function a (){ var a = b =3; })(); 包含了一个函数表达式。在这个代码块中,有以下操作:

  1. var a = b = 3;:这个语句声明了变量 a,但同时也声明了一个全局变量 b 并将其赋值为 3。在非严格模式下,未使用 varletconst 声明的变量会成为全局变量。

  2. console.log("输出a" + (typeof a !== 'undefined')):在这里,你尝试检查变量 a 是否被定义。由于 a 被声明为局部变量,因此在函数外部不可见,typeof a 返回 "undefined",因此整个表达式的结果是 "输出afalse"

  3. console.log("输出b" + (typeof b !== 'undefined')):这里尝试检查变量 b 是否被定义。虽然 b 在函数内部赋值,但由于没有使用 varletconst 来声明它,它成为了一个全局变量,因此在整个页面的作用域内都可见。typeof b 返回 "number"(因为它被赋值为 3),因此整个表达式的结果是 "输出btrue"
    根据非严格模式下的变量作用域和声明方式,代码的输出结果是 "输出afalse""输出btrue"

严格模式下
严格模式下不会输出任何结果,而会抛出错误。
在上面代码块内,有一个立即执行函数 (function a (){ ... })();,它包含了以下行为:

  1. var a = b = 3;:在这行代码中,尝试声明一个局部变量 a,并将其赋值为 b。然而,这里存在一个陷阱。由于没有使用 varletconst 来声明 b,它成为了一个全局变量,而不是局部变量。这意味着 b 的作用域会泄漏到外部的函数作用域(全局作用域),而不是被限制在函数内部。

  2. 在严格模式下,var a 的作用域仅限于函数内部,所以 a 只能在函数内部访问,而在函数外部是不可见的。因此,尝试在函数外部访问 a 时会导致 Uncaught ReferenceError 错误。

  3. 同样,在严格模式下,尝试在函数外部访问全局变量 b 时,也会导致 Uncaught ReferenceError 错误,因为全局变量 b 在非严格模式下会被隐式地创建,但在严格模式下需要显式声明。

所以在定义**var a = b =3;**时就会抛出错误,不会执行打印的语句
在这里插入图片描述

<think>嗯,用户现在想了解JavaScript中如何处理浮点数运算精度问题,特别是0.1+0.2不等于0.3的情况。我之前已经回答过JavaScript的数据类型,其中提到了Number类型,可能用户在实际使用中遇到了浮点计算的错误,所以现在需要具体的解决方案。 首先,我需要回忆一下浮点数精度问题的根源。根据IEEE 754标准,JavaScript使用双精度浮点数表示数字,这导致某些小数无法精确表示,比如0.10.2在二进制中是循环小数,无法准确存储,所以相加后会有微小的误差。这个知识点在引用[1]、[2]、[3]中都有提到,用户可能已经看过这些资料,但需要更详细的解决方法。 接下来,解决方案方面,引用[2]和[4]都提到了使用Number.EPSILON来比较差值是否在误差范围内。这是一个关键点,应该详细说明。另外,用户可能需要知道其他方法,比如放大为整数计算后再缩小,或者使用第三方库如decimal.js,这样可以处理更复杂的运算。 然后,我需要考虑用户的使用场景。可能用户是在做财务计算或需要高精度运算,这时候整数转换法比较实用。而如果项目复杂,使用库会更可靠。此外,用户可能想知道如何判断何时使用哪种方法,所以需要比较不同方法的优缺点。 还要注意用户可能对代码示例有需求,所以应该提供具体的函数示例,比如numbersequal函数,以及整数计算的步骤。同时,引用中的Math.abs(a - b) < Number.EPSILON是否正确,需要确认是否正确应用,避免方向错误。 另外,用户可能想知道为什么Number.EPSILON是2^-52,这涉及到IEEE 754中双精度浮点数的机器精度定义,可能需要简单解释,但不用太深入,保持回答的实用性。 最后,需要总结推荐的方法,帮助用户根据具体情况选择最合适的解决方案。同时,确保回答结构清晰,分点说明,便于用户理解。</think>### JavaScript 浮点数运算精度问题及解决方案 JavaScript 中的浮点数运算(如 `0.1 + 0.20.3`)是由于 **IEEE 754 双精度浮点数标准**的精度限制导致的[^1][^2]。以下是具体原因及解决方法: --- #### 一、问题根源 1. **二进制浮点数的局限性** - 十进制数 `0.1` 和 `0.2` 在二进制中是无限循环小数,无法精确表示。 - 例如: $$0.1_{10} = 0.0001100110011..._2$$ $$0.2_{10} = 0.001100110011..._2$$ - 存储时会进行舍入,导致计算误差[^1][^3]。 2. **实际计算结果** ```javascript console.log(0.1 + 0.2); // 输出 0.30000000000000004 ``` --- #### 二、解决方案 ##### 1. **误差范围比较法(推荐)** 通过判断计算结果的误差是否在可接受范围内(如 `Number.EPSILON`): ```javascript function isEqual(a, b) { return Math.abs(a - b) < Number.EPSILON; } console.log(isEqual(0.1 + 0.2, 0.3)); // true ``` - **原理**:`Number.EPSILON` 是 JavaScript 中最小精度值(约 $2^{-52}$),用于定义浮点数的机器精度[^2][^4]。 ##### 2. **整数放大法** 将小数转换为整数运算,再缩小回小数: ```javascript function addDecimals(a, b) { const factor = Math.pow(10, Math.max(a.toString().split('.')[1]?.length || 0, b.toString().split('.')[1]?.length || 0)); return (a * factor + b * factor) / factor; } console.log(addDecimals(0.1, 0.2)); // 0.3 ``` - **适用场景**:金额计算等需要精确到固定小数位的场景。 ##### 3. **使用第三方库** 对于复杂运算,可借助高精度数学库: - **decimal.js**: ```javascript import { Decimal } from 'decimal.js'; const sum = new Decimal(0.1).add(new Decimal(0.2)); console.log(sum.equals(0.3)); // true ``` - **优点**:支持任意精度计算,避免浮点误差[^3]。 ##### 4. **四舍五入法** 限制小数位数(适用于简单场景): ```javascript const result = (0.1 + 0.2).toFixed(1); // "0.3" console.log(parseFloat(result) === 0.3); // true ``` - **注意**:`toFixed()` 返回字符串,需转换为数字。 --- #### 三、方法对比 | 方法 | 优点 | 缺点 | |--------------------|-------------------------------|-------------------------------| | **误差范围比较** | 简单、标准化 | 仅适用于比较,不适用于计算 | | **整数放大法** | 精确控制计算过程 | 需要手动处理小数位数 | | **第三方库** | 高精度、功能全面 | 增加项目依赖 | | **四舍五入** | 快速解决显示问题 | 可能丢失精度 | --- #### 四、总结 - **推荐方案**: - 比较运算使用 `Number.EPSILON` 误差范围法[^2][^4]。 - 复杂计算使用 `decimal.js` 等库。 - **关键原则**:避免直接比较浮点数,始终通过误差范围或整数运算处理[^1][^3]。 --- ### 相关问题 1. 为什么二进制无法精确表示某些十进制小数? 2. `Number.EPSILON` 的具体数值是多少?它是如何推导的? 3. 除了 `decimal.js`,还有哪些库可以解决浮点数精度问题?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值