javascript 二进制运算技巧解析


Posted in Javascript onNovember 27, 2012

1、原码、反码、补码,正数减法转补码加法
js 在进行二进制运算时,使用 32 位二进制整数,由于 js 的整数都是有符号数,最高位0表示正数,1表示负数,因此,js 二进制运算中使用的整数表达范围是

-Math.pow(2,31) ~ Math.pow(2,31)-1 // -2147483648 ~ 2147483647

原码:最高位 0 表示正,1表示负,其余 31 位是该数的绝对值(真值的绝对值)的二进制形式
反码:正数反码与原码相同,负数反码是原码符号位不变,其余31位取反(0变1,1变0)
补码:正数补码与原码相同,负数补码为反码加 1 (符号位参与运算,其实只有求 -0 的补码才涉及最高位进位,因此不用担心在反码加1时由于符号位参与运算进位而使 - 变 +)。
+0 的反码:32个0 ,按正数处理,原码、反码、补码都是0。
-0 的反码:最高位1,其余位由 +0 原码取反,得到 32 个 1
-0 的补码:其反码是 32 个 1 加 1,最高位溢出被舍弃,得到 32 个0
因此,正负 0 的补码都是 0.
由负数的补码求他的绝对值补码:负二进制数的绝对值,只要各位(包括符号位)取反,再加1,就得到其绝对值。
计算机在处理加减运算时,使用补码进行运算,减法被视为加上一个负数,在处理负数时,用负数的补码进行加法可以即可得到正确运算结果,补码是为了统一加减运算而生的。
正数减法转补码加法的原理是 32 位数溢出:
对于32 位二进制正整数来说,其模为
Math.pow(2,32) = 4294967296

32 位正整数最大表达范围是 4294967296 - 1 ,达到 4294967296 这个值就要进位到33位,33 位是溢出位被丢弃,只得到32 个0(这个道理跟表盘上 0 点 和12 点的时针指在同一个位置是一样的,表盘以 12 为模),因此,一个数逐渐增大,一旦超出 4294967296-1 的数 M 就可以表示为 M%4294967296
而负数 -M (M为绝对值)可以表示为一个正数: 4294967296 - M(这个正数就是负数的补码对应的二进制正整数,负数的补码按32位二进制数,与他的原码相加刚好等于模 ),道理跟表盘一样,11点和负1点指在同一个位置。
以 -3 为例:
(Array(32).join("0")+(3).toString(2)).slice(-32); // |-3| 的二进制数,即原码 
原码 = 00000000000000000000000000000011; 
反码 = 11111111111111111111111111111100; //原码符号位为1,其余位取反 
补码 = 11111111111111111111111111111101; //反码加1因为反码由正数形式的原码低31位取反得到,因此这两个数的低31位全都是1,加上反码符号位1,得到32 个1 
那么,有 
补码+原码 = (反码+1)+原码 
= (反码+原码)+1 
= 1+(32位全是 1 的二进制数) //因为反码由正数形式的原码的低31位取反加上符号位1得到,因此这两个数的和的低31位全都是1,加上反码符号位1,得到32 个1 
= Math.pow(2,32) 
= 4294967296 //这正是32位二进制数的模, 跟 |-1|+11 = 12 原理一样

这就是: 正数减法 -> 负数加法 -> 补码加法 的过程。
2、位运算
因为 js 的整数默认是带符号正数,所以在为运算中,只能使用 31 位,开发者是不能访问最高位的。
位运算只发生在整数上,因此一个非浮点数参与位运算之前会被向下取整。
为了避免访问符号位, javascript 在现实 负数的 二进制时,转换为 符号及 其绝对值的二进制,如:
(-123).toString(2) ;// "-1111011"

按位取反(~): 一元运算, 1 变0,0变1 ,如
~123 ; //-124
可以验证一下这个过程:正数取反,符号位为负,所以结果是一个负数,根据 Math.pow(2,32) - M 可以表示成 -M,可以按下面方法计算
parseInt((Array(32).join(0)+ (123).toString(2)).slice(-32).replace(/\d/g,function(v) { 
return (v*1+1)%2; 
}),2)-Math.pow(2,32) // -124 ,如果是负数减 Math.pow(2,32)

需要注意的是, javascript 位运算都是有符号的,因此达到 32 位,其最高位将作为 符号位,取反时应得到正数(取模 Math.pow(2,32) 的补数--两个数相加得到模,称这两个数互为补数)。
对整数 M 按位取反可以这样算:
((-1*M-1)+Math.pow(2,32))%Math.pow(2,32)

按位与(&):两个数的相同位,都是 1 返回1 ,否则返回0
&234 = 106 
"00000000000000000000000001111011" 
"00000000000000000000000011101010" 
--------------------------------------------- 
"00000000000000000000000001101010"

|234 = 251 
"00000000000000000000000001111011" 
"00000000000000000000000011101010" 
--------------------------------------------- 
"00000000000000000000000011111011"

按位异或(^):两个数的相同位,一个是 1 另一个是 0 则返回 1,否则返回0
^234 = 145 
"00000000000000000000000001111011" 
"00000000000000000000000011101010" 
--------------------------------------------- 
"00000000000000000000000010010001"

异或运算的一些特性:
a ^ a = 0 
a ^ b = b ^ a
 
a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c 
a ^ b ^ a = b 
d = a ^ b ^ c 可以推出 a = d ^ b ^ c //这个特性被用在一些加密算法中 
//如果运算数在适当范围内(不溢出), 如 a=1,b=2,交换两个变量的值 
a = [b,b=a][0] 
//或者 
a = a^b 
b = a^b 
a= a^b

利用位的异或运算使用一个数字记录多个信息:
有几个状态值分别是 1、2、8、16 .....
这些值的规律是,他们的二进制只有一位是 1 ,其余都是 0, 因此, 他们中的任意几个的按位异或运算的结果都不会出现 两个数的某一位都是 1 的情况,并且运算的值都是唯一确定的,也就是,知道运算的结果,就知道是哪几个数的组合,这样可以用一个数字记录多个信息。
00000001 
00000010 
00000100 
00001000 
00010000

1^2^4 = 7 // "00000111"
因此,如果我们知道结果是 7 ,就知道他们是由 1 、2、4 组合而成。
如果我们要设置一个参数,使其包含几个状态值,就可以用 按位或运算,
这样的例子可以参考 PHP 中关于图片类型的几个常量,和 PHP 错误等级定义的几个常量。
这样的例子,也许有用十进制数来描述的,比如: 个位数的数字表示某个属性的状态,十位数的数字表示另一个属性的状态,这样的话,每个状态可以有 10 个值,只用一个数字就可以描述的组合非常多。
左移位(<<) : 一个数的二进制所有位向左移动,符号位不动,高位溢出丢弃,低位补 0
如果不溢出, 左移位的效果是乘以 2。
右移位(>>): 一个数的二进制所有位向右移动,符号位不动,高位补0,低位丢弃
右移位操作的效果是除以 2 并向下取整。
带符号右移(>>>):移位时符号位跟随移动,符号位也作为数值看待,所以,该操作的结果是 32 位无符号整数,因此负数的带符号右移将产生正整数,正数的带符号右移与 无符号右移相同,这是唯一可以操作符号位的运算。
-123>>>1 ;//2147483586
一些要注意的地方
位运算必须是整数,如果运算元不是可用的整数,将取 0 作为运算元
~NaN; // 将执行 ~0 ,结果为 -1 
~'x'; // -1 
'hello'|0; // 0 
({})|0 ; //0

位移运算不能移动超过31位,如果试图移动超过31位,将位数 对32取模后再移位
123>>32 //实际是 123>>0 (32%32 = 0)
123>>33 //实际是 123>>1
32位带符号整数表达范围是 -Math.pow(2,31) ~ Math.pow(2,31)-1 即 -2147483648~2147483647,而 js 数字的精度是双精度,64位,如果一个超过 2147483647 的整数参与位运算的时候就需要注意,其二进制溢出了,截取32位后,如果第32位是1将被解读为负数(补码)。
>>0; //-2147483648 
>>0; //0 
>>0; //-1 
>>0; //1
Javascript 相关文章推荐
用户注册常用javascript代码
Aug 29 Javascript
仅IE不支持setTimeout/setInterval函数的第三个以上参数
May 25 Javascript
jQuery使用动态渲染表单功能完成ajax文件下载
Jan 15 Javascript
使用JavaScript 实现各种跨域的方法
May 08 Javascript
jQuery插件jFade实现鼠标经过的图片高亮其它变暗
Mar 14 Javascript
javascript中indexOf技术详解
May 07 Javascript
AngularJS directive返回对象属性详解
Mar 28 Javascript
js实现hashtable的赋值、取值、遍历操作实例详解
Dec 25 Javascript
Ionic3实现图片瀑布流布局
Aug 09 Javascript
AngularJS 打开新的标签页实现代码
Sep 07 Javascript
微信小程序项目总结之记账小程序功能的实现(包括后端)
Aug 20 Javascript
原理深度解析Vue的响应式更新比React快
Apr 04 Javascript
JavaScript prototype属性深入介绍
Nov 27 #Javascript
Knockoutjs的环境搭建教程
Nov 26 #Javascript
jquery ajax请求实例深入解析
Nov 26 #Javascript
jquery validate poshytip 自定义样式
Nov 26 #Javascript
一个可拖拽列宽表格实例演示
Nov 26 #Javascript
JS编程小常识很有用
Nov 26 #Javascript
javascript:history.go()和History.back()的区别及应用
Nov 25 #Javascript
You might like
有关JSON以及JSON在PHP中的应用
2010/04/09 PHP
使用PHP接收POST数据,解析json数据
2013/06/28 PHP
php获取网页标题和内容函数(不包含html标签)
2014/02/03 PHP
原生php实现excel文件读写的方法分析
2018/04/25 PHP
jquery中邮箱地址 URL网站地址正则验证实例代码
2013/09/15 Javascript
JavaScript中判断整字类型最简洁的实现方法
2014/11/08 Javascript
jQuery根据元素值删除数组元素的方法
2015/06/24 Javascript
jQuery实现的多滑动门,多选项卡效果代码
2016/03/28 Javascript
JavaScript中校验银行卡号的实现代码
2016/12/19 Javascript
JS正则验证多个邮箱完整实例【邮箱用分号隔开】
2017/04/19 Javascript
JavaScript纯色二维码变成彩色二维码
2020/07/23 Javascript
Bootstrap实现翻页效果
2017/11/27 Javascript
文章或博客自动生成章节目录索引(支持三级)的实现代码
2020/05/10 Javascript
[01:09:23]KG vs TNC 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/16 DOTA
python通过zlib实现压缩与解压字符串的方法
2014/11/19 Python
python fabric使用笔记
2015/05/09 Python
深入浅出学习python装饰器
2017/09/29 Python
详解通过API管理或定制开发ECS实例
2018/09/30 Python
从运行效率与开发效率比较Python和C++
2018/12/14 Python
python读取tif图片时保留其16bit的编码格式实例
2020/01/13 Python
在python中使用pymysql往mysql数据库中插入(insert)数据实例
2020/03/02 Python
Python 读取xml数据,cv2裁剪图片实例
2020/03/10 Python
python如何运行js语句
2020/09/09 Python
Django REST Framework 分页(Pagination)详解
2020/11/30 Python
法国购买二手电子产品网站:Asgoodasnew
2020/03/27 全球购物
护士个人简历自荐信
2013/10/18 职场文书
千元咖啡店的创业计划书范文
2013/12/29 职场文书
大学毕业生通用自我评价
2014/01/05 职场文书
简历的自我评价
2014/02/03 职场文书
《与象共舞》教学反思
2014/02/24 职场文书
2014年第四季度入党积极分子思想汇报(十八届四中全会)
2014/11/03 职场文书
领导欢送会主持词
2015/07/06 职场文书
新闻报道稿范文
2015/07/23 职场文书
家庭聚会祝酒词
2015/08/11 职场文书
《莫泊桑拜师》教学反思
2016/02/22 职场文书
详解Apache SkyWalking 告警配置指南
2021/04/22 Servers