双重按位非运算符 ~~ 对数字取整 - xingba-coder
source link: https://www.cnblogs.com/zsxblog/p/17999178
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
双重按位非运算符 ~~ 对数字取整
按位非运算符(~
)将操作数的位反转。它将操作数转化为 32 位的有符号整型。也就是可以对数字进行取整操作(保留整数部分,舍弃小数部分)。
~-2 // 1
~-2.222 // 1
并且按位非运算时,任何数字 x
(已被转化为 32 位有符号整型) 的运算结果都是 -(x + 1)
。
那么双重按位非(~~
)对数字的运算结果就是 -(-(x + 1) + 1)
,结果就是 x
。
所以利用 ~~
操作数字时就可对其进行取整操作(右移操作符 x >> 0
和按位或操作符 x | 0
也有相同作用)。
如果操作的不是 Number
类型的,操作的对象会先转化 Number
类型,下面一起来看看。
操作原始数据类型时
~~(-2.999); // => -2
~~null; // => 0
~~undefined; // => 0
~~0; // => 0
~~(1/0); // => 0
~~false; // => 0
~~true; // => 1
~~'1234' // => 1234
~~'1234asdf' // => 0
~~NaN // => 0
~~
对于不能转化为数字的数据(NaN
) ,操作的结果为 0
右移操作符 >>
和按位或操作符 |
也是如此。
(-2.999) >> 0 // => -2
null >> 0 // => 0
undefined >> 0 // => 0
0 >> 0 // => 0
(1/0) >> 0 // => 0
false >> 0 // => 0
true >> 0 // => 1
'1234' >> 0 // => 1234
'1234asdf' >> 0 // => 0
NaN >> 0 // => 0
(-2.999) | 0 // => -2
null | 0 // => 0
undefined | 0 // => 0
0 | 0 // => 0
(1/0) | 0 // => 0
false | 0 // => 0
true | 0 // => 1
'1234' | 0 // => 1234
'1234asdf' | 0 // => 0
NaN | 0 // => 0
操作对象数据类型时
当 ~~
作用于对象类型时,对象类型会先隐式转化为数字,转化的结果取决于对象的 valueOf
方法和 toString
方法返回的结果。如果对象类型转化后最终的结果是 NaN
,那么 ~~
操作 NaN
则会直接返回 0
。
详细的转换过程:
调用对象的
valueOf
方法:
如果该方法返回一个原始值,JavaScript会尝试将这个原始值转换为一个数字。如果valueOf
方法返回的还是一个对象,JavaScript会继续调用对象的toString
方法。调用对象的
toString
方法:
这个方法返回一个字符串,然后JavaScript会尝试将这个字符串转换为一个数字。转换字符串为数字:
一旦从valueOf
或toString
方法中获得了一个原始值(通常是字符串),JavaScript会按照字符串到数字的转换规则来处理这个值。
如果valueOf
或toString
返回的值是NaN
,那么结果就是NaN
。如果字符串不能被解析为一个有效的数字,结果也是NaN
。 ~~
操作 NaN
返回 0
。
所以也就有下面的结果:
~~{}; // => 0
~~{a:1} // => 0
~~[]; // => 0
~~[1]; // => 1
~~[1,2]; // => 0
对于数组而言,将数组转化为数字,会调用数组的 toString()
。
数组的
toString
方法实际上在内部调用了join()
方法来拼接数组并返回一个包含所有数组元素的字符串,元素之间用逗号分隔。如果join
方法不可用或者不是函数,则会使用Object.prototype.toString
来代替,并返回[object Array]
。
上面的 [1,2] 经过 toString()
后是 '1,2'
, 转为数字则是 NaN
。所以 ~~[1,2]
结果为 0。
下面是对象有自定义的 valueOf()
或者 toString()
情况
var a = {
valueOf:function(){return '11'}, // 字符串'11' 可被转化为 数字 11
toString:function(){return 12}
}
~~a // => 11
var b = {
valueOf:function(){return 'asdf'}, // 字符串'asdf' 转化为 NaN
toString:function(){return 12}
}
~~b // => 0
var c = {
toString:function(){return 12} // 没有 valueOf() ,则调用 toString()
}
~~c // => 12
var d = {
toString:function(){return 'asdf'} // 字符串'asdf' 转化为 NaN
}
~~d // => 0
可进行运算的数字的有效范围
由于 按位运算总是将操作数转换为 32 位整数。 超过 32 位的数字将丢弃其最高有效位。如下例子中(来自MDN),超过 32 位的整数将转换为 32 位整数:
Before: 11100110111110100000000000000110000000000001
After: 10100000000000000110000000000001
再比如 ~~
操作日期类型数据,Date
的 valueOf
方法返回以数值格式表示的一个 Date
对象的原始值,从 1970 年 1 月 1 日 0 时 0 分 0 秒到该日期对象所代表时间的毫秒数。
返回的毫秒数是超过 32 位的整数,不在 ~~
操作的有效范围内,结果就不会是期望的那样。
var date = new Date()
Number(date) // 1706671595364
~~date // 1569578852 结果失真
所以只有对 32位浮点数(经测试,有效范围为:[-2^31,2^31-1]
,即[-2147483648,2147483647]
) 进行按位运算时才会得到期望的结果。
~~2147483647.1 // => 2147483647 正确
~~2147483648.1 // => -2147483648 不正确
~~-2147483648.1 // => -2147483648 正确
~~-2147483649.1 // => 2147483647 不正确
需要注意的是,如果整数部分和小数部分数字之和超过了 16 位(不包括小数点),那么双重按位非操作符的结果也会不正确:( Number编码的精度 的算术会受到舍入的影响。)
对一些函数入参校验及处理方面有用,比如传入的可能是任意数字,需要排除掉极端情况(NaN
,Infinity
),然后取整;
function fn(){
var param = arguments[1]
if(param === 'number' && !isNaN(foo) && foo !== Infinity){
var value = Number(param) || 0;
value = (value < 0)
? Math.ceil(value)
: Math.floor(value);
}
}
使用 ~~
后:
function fn(){
var value = ~~arguments[1]
}
左移(<<)和右移(>>)运算符可以用来进行快速的二进制乘法和除法操作。左移一个数值实际上等于将这个数值乘以2的某个幂,而右移一个数值则等于将这个数值除以2的某个幂(忽略余数)。
但这个使用场景只适用对 2 的乘法除法操作(。。。)
let result = 6 * 2; // 结果是12
let base = 6; // 二进制表示为 110
let shift = 1; // 左移1位
let result = base << shift; // 结果是12,二进制表示为 1100
let result = 12 / 2; // 结果是6,但可能得到一个浮点数 let roundedResult = Math.floor(12 / 2); // 结果是6,确保得到整数
let base = 12; // 二进制表示为 1100
let shift = 1; // 右移1位
let result = base >> shift; // 结果是6,二进制表示为 110
本文探讨了使用双重按位非运算符 ~~
对操作数取整的原理。
~~
之所以可以用来取整,是因为按位运算操作数转化为 32 位的有符号整型,会舍弃掉小数部分。并且按位非运算(~
)时,任何数字x
(已被转化为 32 位有符号整型) 的运算结果都是-(x + 1)
。那么双重按位非(~~
)对数字的运算结果就是-(-(x + 1) + 1)
,结果就是x
。- 操作数是数字并且在位运算的有效范围内(
[-2^31,2^31-1]
),~~
取整才会得到期望的结果。 - 使用场景方面对一些函数入参校验及处理方面可能有用
折腾完毕 🤪。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK