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高级程序设计(第3版)学习笔记6 初识js对象
Oct 11 Javascript
Javascript闭包用法实例分析
Jan 23 Javascript
jQuery实现跨域iframe接口方法调用
Mar 14 Javascript
JavaScript模拟深蓝vs卡斯帕罗夫的国际象棋对局示例
Apr 22 Javascript
Angular中的ng-template及angular 使用ngTemplateOutlet 指令的方法
Aug 08 Javascript
React router动态加载组件之适配器模式的应用详解
Sep 12 Javascript
Vue页面跳转动画效果的实现方法
Sep 23 Javascript
基于mpvue的简单弹窗组件mptoast使用详解
Aug 02 Javascript
解决layer弹出层自适应页面大小的问题
Sep 16 Javascript
对Layer UI 模块化的用法详解
Sep 26 Javascript
js防抖函数和节流函数使用场景和实现区别示例分析
Apr 11 Javascript
js实现模拟购物商城案例
May 18 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 json_encode奇怪问题说明
2011/09/27 PHP
PHP实现的构造sql语句类实例
2016/02/03 PHP
PHP Trait代码复用类与多继承实现方法详解
2019/06/17 PHP
laravel5.5添加echarts实现画图功能的方法
2019/10/09 PHP
jQuery实现购物车多物品数量的加减+总价计算
2014/06/06 Javascript
javascript关于继承的用法汇总
2014/12/20 Javascript
为JS扩展Array.prototype.indexOf引发的问题及解决办法
2015/01/21 Javascript
JavaScript获取网页表单action属性的方法
2015/04/02 Javascript
jQuery里filter()函数与find()函数用法分析
2015/06/24 Javascript
javascript拖拽效果延伸学习
2016/04/04 Javascript
基于javascript实现按圆形排列DIV元素(一)
2016/12/02 Javascript
微信小程序 自定义对话框实例详解
2017/01/20 Javascript
Vue官方文档梳理之全局配置
2017/11/22 Javascript
Bootstrap导航菜单点击后无法自动添加active的处理方法
2018/08/10 Javascript
vue-auto-focus: 控制自动聚焦行为的 vue 指令方法
2018/08/25 Javascript
基于vue-cli npm run build之后vendor.js文件过大的解决方法
2018/09/27 Javascript
JS+CSS实现随机点名(实例代码)
2019/11/04 Javascript
javascript的delete运算符知识点总结
2019/11/19 Javascript
JS实现多选框的操作
2020/06/24 Javascript
[02:08]什么藏在DOTA2 TI9“小紫本”里?斧王历险记告诉你!
2019/05/17 DOTA
python文件操作整理汇总
2014/10/21 Python
Python获取央视节目单的实现代码
2015/07/25 Python
python基础入门学习笔记(Python环境搭建)
2016/01/13 Python
Python的Flask框架中的Jinja2模板引擎学习教程
2016/06/30 Python
python利用正则表达式提取字符串
2016/12/08 Python
python中学习K-Means和图片压缩
2017/11/20 Python
python+opencv识别图片中的圆形
2020/03/25 Python
已安装tensorflow-gpu,但keras无法使用GPU加速的解决
2020/02/07 Python
Python基于yield遍历多个可迭代对象
2020/03/12 Python
浅析Python面向对象编程
2020/07/10 Python
电厂厂长岗位职责
2014/01/02 职场文书
七年级生物教学反思
2014/01/30 职场文书
大学生违纪检讨书300字
2014/10/25 职场文书
贷款担保书
2015/01/20 职场文书
在职证明格式样本
2015/06/15 职场文书
校园之声广播稿
2015/08/18 职场文书