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 无符号右移赋值操作
Apr 17 Javascript
JavaScript 事件记录使用说明
Oct 20 Javascript
JavaScript实现LI列表数据绑定的方法
Aug 04 Javascript
jQuery树形下拉菜单特效代码分享
Aug 15 Javascript
如何利用AngularJS打造一款简单Web应用
Dec 05 Javascript
JS中正则表达式要注意lastIndex属性
Aug 08 Javascript
vue 数组和对象不能直接赋值情况和解决方法(推荐)
Oct 25 Javascript
vue2.0 兄弟组件(平级)通讯的实现代码
Jan 15 Javascript
微信小程序实现天气预报功能
Jul 18 Javascript
angular6 利用 ngContentOutlet 实现组件位置交换(重排)
Nov 02 Javascript
vue2中引用及使用 better-scroll的方法详解
Nov 15 Javascript
Vue.js 无限滚动列表性能优化方案
Dec 02 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
整合了前面的PHP数据库连接类~~做成一个分页类!
2006/11/25 PHP
PHP为表单获取的URL 地址预设 http 字符串函数代码
2010/05/26 PHP
PHP缓存技术的使用说明
2011/08/06 PHP
php 检查电子邮件函数(自写)
2014/01/16 PHP
php导出excel格式数据问题
2014/03/11 PHP
Symfony2开发之控制器用法实例分析
2016/02/05 PHP
Zend Framework处理Json数据方法详解
2016/12/09 PHP
几个常用的JavaScript字符串处理函数 - split()、join()、substring()和indexOf()
2009/06/02 Javascript
firefo xml 读写实现js代码
2009/06/11 Javascript
JavaScript弹簧振子超简洁版 完全符合能量守恒,胡克定理
2009/10/25 Javascript
js下写一个事件队列操作函数
2010/07/19 Javascript
使用jQuery validate 验证注册表单实例演示
2013/03/25 Javascript
基于js实现checkbox批量选中操作
2016/11/22 Javascript
jQuery实现三级联动效果
2017/03/02 Javascript
JS简单判断字符在另一个字符串中出现次数的2种常用方法
2017/04/20 Javascript
JavaScript递归算法生成树形菜单
2017/08/15 Javascript
Iphone手机、安卓手机浏览器控制默认缩放大小的方法总结(附代码)
2017/08/18 Javascript
解决vue v-for 遍历循环时key值报错的问题
2018/09/06 Javascript
小程序视频或音频自定义可拖拽进度条的示例代码
2018/09/30 Javascript
Vue初始化中的选项合并之initInternalComponent详解
2020/06/11 Javascript
Python对两个有序列表进行合并和排序的例子
2014/06/13 Python
Saltstack快速入门简单汇总
2016/03/01 Python
Python Pandas分组聚合的实现方法
2019/07/02 Python
Python任务调度模块APScheduler使用
2020/04/15 Python
利用CSS3的flexbox实现水平垂直居中与三列等高布局
2016/09/12 HTML / CSS
HTML5 MiranaVideo播放器 (代码开源)
2010/06/11 HTML / CSS
HTML5 manifest离线缓存的示例代码
2018/08/08 HTML / CSS
英国家电购物网站:Sonic Direct
2019/03/26 全球购物
OSPF有什么优点?为什么OSPF比RIP收敛快?
2013/02/13 面试题
土木工程毕业生自荐信
2013/09/21 职场文书
十岁生日同学答谢词
2014/01/19 职场文书
给孩子的新年寄语
2014/04/08 职场文书
银行竞聘演讲稿
2014/05/16 职场文书
个人租房协议书
2014/11/28 职场文书
学生党支部工作总结2015
2015/05/26 职场文书
一个成功的互联网创业项目,必须满足这些要求
2019/08/23 职场文书