从JavaScript的移位运算看数字在计算机内部的编码——补码
偶然看到一个JavaScript的题目:
js中13>>2=? -13>>2=?
在浏览器中很容易测试出答案分别是 3 和 -4。
13>>2 = 3 很好理解,但是对于 -13>>2 = -4 ,我无法理解。然后我又去用 Java 语言实现一遍,结果也是一样的。
我知道关于 “原码、反码、补码” 这个知识点是 《计算机组成原理》 中的内容,但苦于网上下载不到该书,只得去Google各种博客去深入了解。
(吐槽下,Google搜索“原码 反码 补码”的结果,比度娘搜索该关键词的结果好太多!用百度真真是浪费生命。)
看着充满数学公式,和无聊的 “计算方法”,都是些教材上的理论东西,越看越迷糊。后来,我就总结了:
原码、反码、补码
其实,抛开应付《计算机组成原理》考试,以后(无论笔试、面试、Coding)遇到的都是 “补码”,没人关心 原码 和 反码 是个什么。
为什么呢? 因为 在计算机内部(寄存器层面),数都是以补码的形式存储在寄存器中的。
原码,反码什么的都是为了计算补码存在于人类思维中的,可无视它们。
比如,此题中,13 是原始数,移位移的是 13 的补码(下面细讲JavaScript整数的补码),移位后再将 补码 反算成 原始数。
补码的简便计算方法
无论C/C++/Java强类型语言,还是JavaScript,都有移位运算符,在CPU中的运算器进行移位操作时,这个过程都是一样的。
这里为了方便,假设 在一个寄存器只有8位的机器上,整型只能用 8个比特位来表示。本文讲的都是有符号数。
那么,
13的 补码 就可以表示为:(其中D 表示 十进制数, B 表示二进制数)
13 (D) = 0000 1101 (B)
-13 的 补码 就是:
-13 (D) = 1101 0011 (B)
那么 -13 的补码是怎么算出来的呢? 很简单,只要 让 -13 的补码 加上 13 的补码, 溢出后变成 0 就行了。
这有什么根据么? 当然有根据了,我们知道为什么发明 补码么? 就是因为 原码 和 反码 会出现 +0 和 -0 ,
所以才不为计算机科学家采用。而接着发明出来的补码,恰好补上这一漏洞,使得计算机可以和正常的数学运算
一样,完美的完成加减乘除。
所以,如果 在数学里,
13 + (-13) = 0
那么,在计算机的世界里,必须
0000 1101 + 1101 0011 = 0
因为造出补码的意义就是为了让计算机内补码和数学中的数字一一对应,就是能完全代表数学中的数。这样才能使用传承了
几千年的数学知识进行更高深的微积分等科学计算。
这里举一个简单的例子。
一台8位寄存器的机器,13 的补码 是 0000 1101 ,那 -13 的补码呢?
根据 -13 + 13 = 0 ,可以这么算:
0000 1101
+ xxxx xxxx
———————
1 0000 0000 (8寄存器,进位的1溢出被丢弃。寄存器中就保存的是 0000 0000 ,就是数学中的0)
这样,很容易凑出 -13 的补码,再简单不过了。就只有1和0进行加减。-13的补码可以算出 是 1111 0011。
比之,教科书式的
(这么头大的公式,你会去用,这明显是数学家们为了论述严密而显摆给学生们看的)
和一般教学中用的:
负数的补码,是反码加1。而反码又是原码,符号位不变,加1。
(差不多,只是多了个抽象概念。反码没有必要记忆,时代遗留产物,大胆抛弃)
>> << 移位运算
算出了 13 和 -13 的补码,接下来就是 右移 2 位了。
无论什么语言,只要是在X86的机器,其移位运算在汇编层都是通过 算术右移指令SAR(shift arithmetic right)执行的。而算术右移指令的
具体操作是,将寄存器中的数值(就是1和0的数串)右移,最左端用最高位填充,而不是补零。这是X86汇编规定的,至于为什么有些原因,不赘述。
那么, 13 的补码右移 2 位后是:
13的补码: 13 (D) = 0000 11 (B)
13的补码右移2位后: 0000 0011 (最右边的01被丢掉)
-13的补码: -13 (D) = 1111 00 (B)
-13的补码右移2位后: 1111 1100 (最右边的11被丢掉)
移位后反算出数值
移位完了,并不代表结束了。移位后的数字还在寄存器中,是未知数(我们要求的未知数)的补码的数串。
然后,我们要根据补码,算出该数来。
依据《计算机组成原理》:正数是原码、反码、补码三码合一。
13 的补码右移2位后的数串(未知数的补码形式),换算成十进制,就是 3。
13 移位后的补码容易换成十进制(因为是正数,所以很容易算出来),而 -13 的移位后的补码究竟是十进制的多少,就要小算一下。
用上面的方法你能算出来吗?
计算过程:
-13的补码右移2位后: 1111 1100
+ xxxx xxxx
———————
1 0000 0000
凑出 一个加数来,应该很简单吧。第二个加数xxxx xxxx就是 0000 0100 ,就是 十进制的 4。
就是说 1111 1100 是 -4 , -13移位后的补码是 十进制的 -4 。
答案就出来了。 13 >> 2 等于 3, -13 >> 等于 -4.
再说JavaScript
前面说的是 8位 的机器,数字都是用8个比特位来表示。再出现C/C++/Java等高级语言后,数据类型的宽度由语言本身决定,如C/C++的int/long由具体实现语言标准的编译器决定每个数据类型到底需要多少个比特位,而Java则因为有JVM的存在,整型统一为32位。
那在JavaScript中,这个整数到底是用多少个比特位表示呢?。JavaScritp 是这样的:
根据这个问答中有人讲:
在
Javascript权威指南 第六版 3.1小节有讲到:
Number类型统一按浮点数处理,64位存储,整数是按最大54位来算最大最小数的,否则会丧失精度;某些操作(如数组索引还有位操作)是按32位处理的~~
浮点数范围as large as±1.7976931348623157×10的308次方
as small as±5×10的−324次方精确整数范围:
TheJavaScript number format allows you to exactly represent all integers between
−9007199254740992and9007199254740992(即正负2的53次方)数组索引还有位操作:
正负2的31次方
鉴于大家可能没书,下载PDF也费事,就贴出图片来。
根据图片,32位整数,就是说题中的 13 在JavaScript 中是如下表示的:
13 = 0000 0000 0000 0000 0000 0000 0000 1101 (13的补码)
字节编号 1 2 3 4 5 6 7 8
-13 = 1111 1111 1111 1111 1111 1111 1111 0011 (-13的补码)
当然,也有另外一种简单计算补码的方法:
负数的补码这么记简单。
符号位不变。其他的从低位开始,指导遇见第一个1之前,什么都不变。遇见第一个1后保留这个1,以后按位取反。
例:[-7]原= 1 0000111 B
[-7]补= 1 1111001 B
技巧性很强,千万别记错了。
反正我的方法就是:记住正数和负数的补码加起来也是0就对了。(而正数是三码合一)
另附一个有趣小问题:
你知道下面的C代码会出现什么问题吗?(注意移位操作)
#include <stdlib.h>
#include <stdio.h> static void divide_by_two(int num)
{
while (num) {
printf("%d\n", num);
num = num>>;
}
} int main()
{
int num;
scanf("%d", &num); divide_by_two(num); return ;
}
答案见:http://blog.chinaunix.net/uid-23629988-id-3018793.html
PS:在JS中,位运算基本没用。这是强类型语言C/C++/Java才经常用到的内容。
.
从JavaScript的移位运算看数字在计算机内部的编码——补码的更多相关文章
- Java中的位运算符、移位运算
一.位运算 Java中有4个位运算,它们的运算规则如下: (1)按位与 (&) :两位全为1,结果为1,否则为0: (2)按位或 (|) :两位有一个为1,结果为1,否则为0: (3) ...
- 【原创】Java移位运算
学习移位运算,首先得知道参与移位运算的类型的位数,那先来复习下Java基础类型的占位数吧. Java基础类型 Java基础类型总结一览表 类型 二进制位数 最大值 最小值 初始化值 表示形式 带符号 ...
- 【JavaScript】深入分析JavaScript的关系运算和if语句
JavaScript的关系运算,没有我原想的那么简单.等终于理清它的运算逻辑之后,我的头大了至少一圈.而if语句的真假判定逻辑本身不难,但要把它和关系运算联系起来,相信你会和我一样,到达崩溃边缘.不信 ...
- << 移位运算
/** * The maximum capacity, used if a higher value is implicitly specified * by either of the constr ...
- 浅谈JavaScript浮点数及其运算
原文:浅谈JavaScript浮点数及其运算 JavaScript 只有一种数字类型 Number,而且在Javascript中所有的数字都是以IEEE-754标准格式表示的.浮点数的精度问题 ...
- java 移位运算
移位运算 :将整数转化为二进制(以补码的形式),按位平移. << 左移 >> 右移 >>> 无符号右移 << 右移: 按位做平 ...
- JavaScript 浮点数及运算精度调整总结
JavaScript 浮点数及运算精度调整总结 JavaScript 只有一种数字类型 Number,而且在Javascript中所有的数字都是以IEEE-754标准格式表示的.浮点数的精度问题不是J ...
- java中位运算和移位运算详解
一.位运算 (1)按 位 与 & 如果两个相应的二进制形式的对应的位数都为1,则结果为1,记为同1为1,否则为0.首先我们看一下对正数的运算 分别看一下正数和负数的具体运算步骤 ...
- BUG 记录:移位运算与扩展欧几里得算法
BUG 记录:移位运算与扩展欧几里得算法 起因 上个月就开始打算用C++写一个ECC的轮子(为什么?折磨自己呗!),奈何自己水平有点差,拖到现在才算写完底层的大数运算.在实现欧几里得算法的时候,我开始 ...
随机推荐
- C# 内存管理优化实践
内存优化畅想系列文章已经结束了,很多读者读完之后可能觉得“然并卵”,毕竟都是给微软提的建议而已,现在都没有实现.那么为了优化内存,有没有什么我们现在就能用的技巧呢?我的答案是:有.网上关于.net内存 ...
- plsql 显式游标
显式游标的处理过程包括: 声明游标,打开游标,检索游标,关闭游标. 声明游标 CURSOR c_cursor_name IS statement; 游标相当于一个查询结果集,将查询的结果放在游标里,方 ...
- 彻底删除mysql的方法(有隐藏文件)
1.建议使用360进行卸载,可以彻底卸载软件 2.360会提醒删除注册表 3.这个隐藏文件要删除掉 在 C:\Documents and Settings\ 路径下搜索 MySQL 文件夹(默认隐藏的 ...
- IIS7.5 asp+access数据库连接失败处理 64位系统
IIS7.5 asp+access数据库连接失败处理(SRV 2008R2 x64/win7 x64) IIS7.5不支持oledb4.0驱动?把IIS运行模式设置成32位就可以了,微软没有支持出64 ...
- PHP 正则表达式匹配 preg_match 与 preg_match_all 函数
--http://www.5idev.com/p-php_preg_match.shtml 正则表达式在 PHP 中的应用 在 PHP 应用中,正则表达式主要用于: 正则匹配:根据正则表达式匹配相应的 ...
- Visual C#实现Windows信使服务
现在有很多网络管理软件都具备网络上信息实时传送的功能,虽然有些网络通讯软件功能比较强大,有的软件不仅可以传送文本信息,还可以传送二进制文件等.但 它们都有一个无法克服的缺点,那就是分发比较困难,信息传 ...
- [转]Web UI 设计命名规范
来源:http://blog.bingo929.com/web-ui-design-name-convention.html 一.网站设计及基本框架结构: 1. Container “conta ...
- VisualStudio2015内置LocalDB
简直坑爹,我将之前的VS2013的工程迁移到新电脑的VS2015,然后用的本地数据库居然连接报错了,然后我试连了一下本地数据库, 就是Tools-->Connect to Databases-- ...
- Linux 关于解压
1.*.tar 用 tar –xvf 解压 2.*.gz 用 gzip -d或者gunzip 解压 3.*.tar.gz和*.tgz 用 tar –xzf 解压 4.*.bz2 用 bzip2 -d或 ...
- SGU 163.Wise King
一道题目长的水题.... 总结就一句话,给出n个(-3~3)的数,一个数m,取任意个数是使这些数的m次幂之和最大. code #include <iostream> #include &l ...