x 等价于 math.trunc(x),即向零取整,非负数时看似向下取整,但负数时(如 (-3.7) 得 -3)与 math.floor(-3.7) 得 -4 不同,误用易引发逻辑错误。

JavaScript 中利用按位取反(~)与双按位取反(~~)实现向下取整,本质是借助位运算将数字强制转为 32 位有符号整数,并利用其截断小数的特性。它不是真正意义上的“数学向下取整”(如 Math.floor(-1.8) 得 -2),而是在**非负数范围内等价于向下取整,在负数上表现等同于向零取整(即 Math.trunc)**。理解这点是避免误用的关键。
为什么 ~~x 看起来像向下取整?
按位取反操作符 ~ 会先将操作数转换为 32 位有符号整数(执行 ToInt32 抽象操作),再对每一位取反。该转换过程会**直接丢弃小数部分**(不四舍五入、不向上/向下),也就是向零截断:
-
ToInt32(3.9)→3 -
ToInt32(-3.9)→-3
由于 ~x === -(x + 1)(对任意整数成立),所以连续两次取反:~~x === ~(-(x + 1)) === x —— 对已为整数的 x 恒等。
但对浮点数 y,实际发生的是:~~y === ~~(ToInt32(y)) === ToInt32(y)。
因此,~~x 的效果就是 Math.trunc(x)。
和 Math.floor 的关键区别(尤其负数)
这是最容易踩坑的地方:
-
~~3.7→3,Math.floor(3.7)→3✅ 一致 -
~~-3.7→-3,Math.floor(-3.7)→-4❌ 不同!
若业务逻辑要求对负数也向下取整(比如计算页码、索引偏移),~~ 会出错。此时必须用 Math.floor 或其他明确语义的方法。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
适用场景与注意事项
仅在满足以下全部条件时,才考虑用 ~~:
- 输入确定为**非负数**(如
index,length,Date.now()计算结果等) - 目标是快速丢弃小数(向零截断),且可接受
NaN→0、Infinity→0等 ToInt32 的边界行为 - 代码处于性能敏感路径(如 WebGL 循环、大量数值处理),且已实测
~~带来可衡量提升(现代 JS 引擎中差距往往极小) - 团队成员熟悉该写法,或有清晰注释说明其等价于
Math.trunc而非Math.floor
⚠️ 注意:~~ 对 null、undefined、空字符串等会转为 0,而 Math.floor 会返回 NaN,行为差异显著。
更安全、更现代的替代方案
除非有强性能需求且上下文受控,推荐使用语义明确的原生方法:
- 需要向零取整(兼容
~~)→ 直接用Math.trunc(x)(ES6+,语义清晰) - 需要真正向下取整(含负数)→ 用
Math.floor(x) - 需要类型安全的整数转换(如 DOM 尺寸)→
Math.round(x) | 0或显式parseInt(x, 10)(注意parseInt的字符串隐式转换)
现代引擎对 Math.trunc 和 Math.floor 的优化已非常成熟,可读性与正确性远胜于位运算技巧。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










