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 相关文章推荐
jquery中输入验证中一个不错的效果
Aug 21 Javascript
判断window.onload是否多次使用的方法
Sep 21 Javascript
分享一个自己写的简单的javascript分页组件
Feb 15 Javascript
javascript字符串循环匹配实例分析
Jul 17 Javascript
js 右侧浮动层效果实现代码(跟随滚动)
Nov 22 Javascript
JSON遍历方式实例总结
Dec 07 Javascript
js仿3366小游戏选字游戏
Apr 14 Javascript
深入理解jQuery事件绑定
Jun 02 Javascript
jQuery.cookie.js实现记录最近浏览过的商品功能示例
Jan 23 Javascript
ES6新特性之函数的扩展实例详解
Apr 01 Javascript
Angular4.0中引入laydate.js日期插件的方法教程
Dec 25 Javascript
Vue自定义铃声提示音组件的实现
Jan 22 Vue.js
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
Laravel 5.3 学习笔记之 错误&amp;日志
2016/08/28 PHP
php使用ftp远程上传文件类(完美解决主从文件同步问题的方法)
2016/09/23 PHP
php实现产品加入购物车功能(1)
2020/07/23 PHP
phpinfo的知识点总结
2019/10/10 PHP
javascript中callee与caller的用法和应用场景
2010/12/08 Javascript
纯js简单日历实现代码
2013/10/05 Javascript
浅析document.ready和window.onload的区别讲解
2013/12/18 Javascript
javascript为下拉列表动态添加数据项
2014/05/23 Javascript
JavaScript生成的动态下雨背景效果实现方法
2015/02/25 Javascript
原生JavaScript实现瀑布流布局
2020/06/28 Javascript
深入理解js中this的用法
2016/05/28 Javascript
jquery自动补齐功能插件flexselect用法示例
2016/08/06 Javascript
js中 计算两个日期间的工作日的简单实例
2016/08/08 Javascript
javascript使用闭包模拟对象的私有属性和方法
2016/10/05 Javascript
JS 滚动事件window.onscroll与position:fixed写兼容IE6的回到顶部组件
2016/10/10 Javascript
JS高级运动实例分析
2016/12/20 Javascript
jQuery学习之DOM节点的插入方法总结
2017/01/22 Javascript
angularjs定时任务的设置与清除示例
2017/06/02 Javascript
javascript  数组排序与对象排序的实例
2017/07/17 Javascript
React 实现拖拽功能的示例代码
2019/01/06 Javascript
Vue 实例事件简单示例
2019/09/19 Javascript
vue实现跳转接口push 转场动画示例
2019/11/01 Javascript
[44:40]2018DOTA2亚洲邀请赛3月30日 小组赛A组Liquid VS OG
2018/03/31 DOTA
python实现时间o(1)的最小栈的实例代码
2018/07/23 Python
Python 获取 datax 执行结果保存到数据库的方法
2019/07/11 Python
CSS3的first-child选择器实战攻略
2016/04/28 HTML / CSS
利用css3径向渐变做一张优惠券的示例
2018/03/22 HTML / CSS
Farah官方网站:男士服装及配件
2019/11/01 全球购物
什么是接口(Interface)?
2013/02/01 面试题
销售人员职业生涯规划范文
2014/03/01 职场文书
后勤服务中心总经理工作职责
2014/03/03 职场文书
纪念九一八事变演讲稿:勿忘国耻
2014/09/14 职场文书
瘦西湖导游词
2015/02/03 职场文书
家长必看:义务教育,不得以面试 评测等名义选拔学生
2019/07/09 职场文书
给numpy.array增加维度的超简单方法
2021/06/02 Python
分布式Redis Cluster集群搭建与Redis基本用法
2022/02/24 Redis