JavaScript常用进制转换及位运算实例解析


Posted in Javascript onOctober 14, 2020

前言

在一般的代码中很少会接触到进制和位运算,但这不代表我们可以不去学习它。作为一位编程人员,这些都是基础知识。如果你没有学过这方面的知识,也不要慌,接下来的知识并不会很难。本文你将会学习到:

  • 进制转换
  • 按位操作符
  • JavaScript进制转换
  • 手动实现进制转换

进制转换

以下使用常见的十进制和二进制转换作为例子,其他进制的转换也是大同小异,感兴趣可以自己琢磨下。

十进制转二进制

根据 “逢十进一” 的法则进行计数时,每十个相同的单位组成一个和它相邻的较高的单位,这种计数法叫做十进制计数法,简称十进制。这种是我们最常用的计数法。

整数

整数使用 “除二取余,逆序排列” 来转换为二进制,下面是18转换为二进制的例子:

// 除二取余
18 / 2 = 9...0
9 / 2 = 4...1
4 / 2 = 2...0
2 / 2 = 1...0
1 / 2 = 0...1

// 倒序排列
10010

就这么简单,将得出的余数逆序排列,即可得出18的二进制表示

小数

小数使用的是 “乘二取整,顺序排列”,由于方法不同需要分开计算。下面是16.125转为二进制的例子:

16 / 2 = 8...0
8 / 2 = 4...0
4 / 2 = 2...0
2 / 2 = 1...0
1 / 2 = 0...1

0.125 * 2 = 0.25
0.25 * 2 = 0.5
0.5 * 2 = 1

10000.001

将小数相乘的结果,取结果的整数顺序排列,得出小数位的二进制表示

二进制转十进制

根据 “逢二进一 ” 的法则进行计数时,每两个相同的单位组成一个和它相邻的较高的单位,这种计数法叫做二进制计数 法,简称二进制。用二进制计数时,只需用两个独立的符号“0”和“1” 来表示。

整数

整数使用 “按权相加” 法,即二进制数首先写成加权系数展开式,然后按十进制加法规则求和。下面是101010转换位十进制的例子:

2^5 2^4 2^3 2^2 2^1 2^0
1 0 1 0 1 0
------------------------
32 + 0 + 8 + 0 + 2 + 0 = 42

上面从右数依次是2的0次方,2的1次方,2的2次方... , 只取位数为1的结果,将它们相加就可以得到十进制。

小数

10110.11转十进制:

2^4 2^3 2^2 2^1 2^0 2^-1 2^-2
1 0 1 1 0 . 1 1
-------------------------------
16 + 0 + 4 + 2 + 0 + 0.5 + 0.25 = 22.75

按位操作符

按位操作符(Bitwise operators) 将其操作数(operands)当作32位的比特序列(由0和1组成),前 31 位表示整数的数值,第 32 位表示整数的符号,0 表示正数,1 表示负数。例如,十进制数18,用二进制表示则为10010。按位操作符操作数字的二进制形式,但是返回值依然是标准的JavaScript数值。

按位与( AND)

对于每一个比特位,只有两个操作数相应的比特位都是1时,结果才为1,否则为0。
用法: a & b 。

9 (base 10) = 00000000000000000000000000001001 (base 2)
14 (base 10) = 00000000000000000000000000001110 (base 2)
--------------------------------
14 & 9 (base 10) = 00000000000000000000000000001000 (base 2) = 8 (base 10)

在判断一个数字奇偶时,可以使用 a & 1

function assert(n) {
return n & 1 ? "奇数" : "偶数"
}
assert(3) // 奇数

因为奇数的二进制最后一位是1,而1的二进制最后一位也是1,通过 & 操作符得出结果为1

按位或(OR)

对于每一个比特位,当两个操作数相应的比特位至少有一个1时,结果为1,否则为0。
用法: a | b

9 (base 10) = 00000000000000000000000000001001 (base 2)
14 (base 10) = 00000000000000000000000000001110 (base 2)
--------------------------------
14 | 9 (base 10) = 00000000000000000000000000001111 (base 2) = 15 (base 10)

将浮点数向下取整转为整数,可以使用 a | 0

12.1 | 0 // 12
12.9 | 0 // 12

按位异或(XOR)

对于每一个比特位,当两个操作数相应的比特位有且只有一个1时,结果为1,否则为0。
用法: a ^ b

9 (base 10) = 00000000000000000000000000001001 (base 2)
14 (base 10) = 00000000000000000000000000001110 (base 2)
--------------------------------
14 ^ 9 (base 10) = 00000000000000000000000000000111 (base 2) = 7 (base 10)

按位非(NOT)

反转操作数的比特位,即0变成1,1变成0。

用法: ~ a

9 (base 10) = 00000000000000000000000000001001 (base 2)
--------------------------------
~9 (base 10) = 11111111111111111111111111110110 (base 2) = -10 (base 10)

通过两次反转操作,可将浮点数向下取整转为整数

~~16.125 // 16
~~16.725 // 16

左移(Left shift)

将 a 的二进制形式向左移 b (< 32) 比特位,右边用0填充。
用法: a << b

9 (base 10): 00000000000000000000000000001001 (base 2)
--------------------------------
9 << 2 (base 10): 00000000000000000000000000100100 (base 2) = 36 (base 10)

左移一位相当于在原数字基础上乘2,利用这一特点,实现2的n次方:

function power(n) {
return 1 << n
}
power(3) // 8

有符号右移

将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位。

用法: a >> b

9 (base 10): 00000000000000000000000000001001 (base 2)
--------------------------------
9 >> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)

相比之下, -9 >> 2 得到 -3,因为符号被保留了。

-9 (base 10): 11111111111111111111111111110111 (base 2)
--------------------------------
-9 >> 2 (base 10): 11111111111111111111111111111101 (base 2) = -3 (base 10)

与左移相反,右移一位在原数字基础上除以2

64 >> 1 // 32

无符号右移

将 a 的二进制表示向右移 b (< 32) 位,丢弃被移出的位,并使用 0 在左侧填充。

用法: a >>> b

在非负数来说, 9 >>>2 和 9 >> 2 都是一样的结果

9 (base 10): 00000000000000000000000000001001 (base 2)
--------------------------------
9 >>> 2 (base 10): 00000000000000000000000000000010 (base 2) = 2 (base 10)

而对于负数来说,结果就大有不同了,因为 >>> 不保留符号,当负数无符号右移时,会使用0填充

-9 (base 10): 11111111111111111111111111110111 (base 2)
--------------------------------
-9 >>> 2 (base 10): 00111111111111111111111111111101 (base 2) = 1073741821 (base 10)

可以使用无符号右移来判断一个数的正负

function isPos(n) {
  return (n === (n >>> 0)) ? true : false;
}

isPos(-1); // false
isPos(1); // true

虽然 -1 >>> 0 不会发生右移,但 -1 的二进制码已经变成了正数的二进制码, -1 >>> 0 结果为4294967295

Javascript进制转换

toString

toString 常用于将一个变量转为字符串,或是判断一个变量的类型,例如:

let arr = []
Object.prototype.toString.call(arr) // [object Array]

你应该没想过 toString 可以用于进制转换,请看下面例子:

(18).toString(2) // 10010(base 2)
(18).toString(8) // 22 (base 8)
(18).toString(16) // 12 (base 16)

参数规定表示数字的基数,是 2 ~ 36 之间的整数,若省略该参数,则使用基数 10。该参数可以理解为转换后的进制表示。

parseInt

parseInt 常用于数字取整,它同样可以传入参数用于进制转换,请看下面例子:

parseInt(10010, 2) // 18 (base 10)
parseInt(22, 8) // 18 (base 10)
parseInt(12, 16) // 18 (base 10)

第二个参数表示要解析的数字的基数,该值介于 2 ~ 36 之间。如果省略该参数或其值为 0,则数字将以 10 为基础来解析。如果该参数小于 2 或者大于 36,则 parseInt 将返回 NaN。

记得有道面试题是这样的:

// 问:返回的结果
[1, 2, 3].map(paseInt)

接下来,我们来一步一步的看下过程发生了什么?

parseInt(1, 0) // 基数为 0 时,以 10 为基数进行解析,结果为 1
parseInt(2, 1) // 基数不符合 2 ~ 36 的范围,结果为 NaN
parseInt(3, 2) // 这里以 2 为基数进行解析,但 3 很明显不是一个二进制表示,故结果为 NaN

//题目结果为
[1, NaN, NaN]

手动实现进制转换

虽然 JavaScript 为我们内置了进制转换的函数,但手动实现进制转换有利于我们理解过程,提高逻辑能力。对于初学者来说也是一个很不错的练习例子。以下只简单实现非负整数的转换。

十进制转二进制

基于 “除二取余” 思路实现

function toBinary(value) {
  if (isNaN(Number(value))) {
    throw `${value} is not a number` 
  }
  let bits = []
  while (value >= 1) {
    bits.unshift(value % 2)
    value = Math.floor(value / 2)
  }
  return bits.join('')
}

使用

toBinary(36) // 100100
toBinary(12) // 1100

二进制转十进制

基于 “取幂相加” 思路实现

function toDecimal(value) {
 let bits = value.toString().split('')
 let res = 0
 while (bits.length) {
  let bit = bits.shift()
  if (bit == 1) {
   // ** 为幂运算符,如:2**3 为 8
   res += 2 ** bits.length
  }
 }
 return res
}

使用

toDecimal(10011) // 19
toDecimal(11111) // 33

写在最后

本文为大家介绍了进制和位运算的相关知识,旨在温故知新。我们只需要大概了解就好,因为在开发中真的用得少,至少我只用过 ~~ 来取整。而类似于~~这种取整操作还是尽量少用为好,对于其他开发者来说,可能会影响到代码可读性。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Js 本页面传值实现代码
May 17 Javascript
BootStrap智能表单demo示例详解
Jun 13 Javascript
js实现上传文件添加和删除文件选择框
Oct 24 Javascript
基于JS实现checkbox全选功能实例代码
Oct 31 Javascript
深入浅析Vue不同场景下组件间的数据交流
Aug 15 Javascript
JavaScript实现打印星型金字塔功能实例分析
Sep 27 Javascript
实战node静态文件服务器的示例代码
Mar 08 Javascript
p5.js入门教程之小球动画示例代码
Mar 15 Javascript
JS实现横向轮播图(中级版)
Jan 18 Javascript
JavaScript对象原型链原理详解
Feb 05 Javascript
JS+canvas五子棋人机对战实现步骤详解
Jun 04 Javascript
Vue实现摇一摇功能(兼容ios13.3以上)
Jan 26 Vue.js
Vue实现鼠标经过文字显示悬浮框效果的示例代码
Oct 14 #Javascript
JavaScript本地储存:localStorage、sessionStorage、cookie的使用
Oct 13 #Javascript
原生JS实现相邻月份日历
Oct 13 #Javascript
jquery简易手风琴插件的封装
Oct 13 #jQuery
原生js实现照片墙效果
Oct 13 #Javascript
js轮播图之旋转木马效果
Oct 13 #Javascript
Vue中正确使用Element-UI组件的方法实例
Oct 13 #Javascript
You might like
PHP获取文件绝对路径的代码(上一级目录)
2011/05/29 PHP
xml在joomla表单中的应用详解分享
2012/07/19 PHP
如何利用PHP执行.SQL文件
2013/07/05 PHP
用dom+xhtml+css制作的一个相册效果代码打包下载
2008/01/24 Javascript
javascript 短路法代码精简
2009/08/20 Javascript
Javascript中的for in循环和hasOwnProperty结合使用
2013/06/05 Javascript
模拟电子签章盖章效果的jQuery插件源码
2013/06/24 Javascript
jquery 单引号和双引号的区别及使用注意
2013/07/31 Javascript
基于jquery的禁用右键、文本选择功能、复制按键的实现代码
2013/08/27 Javascript
Tab切换组件(选项卡功能)实例代码
2013/11/21 Javascript
创建、调用JavaScript对象的方法集锦
2014/12/24 Javascript
jquery.validate使用时遇到的问题
2015/05/25 Javascript
jQuery实时显示鼠标指针位置和键盘ASCII码
2016/03/28 Javascript
Vue实现动态响应数据变化
2017/04/28 Javascript
利用jsonp解决js读取本地json跨域的问题
2018/12/11 Javascript
Element-ui自定义table表头、修改列标题样式、添加tooltip、:render-header使用
2019/04/11 Javascript
详解如何模拟实现node中的Events模块(通俗易懂版)
2019/04/15 Javascript
如何使用Node.js爬取任意网页资源并输出PDF文件到本地
2019/06/17 Javascript
nuxt+axios实现打包后动态修改请求地址的方法
2020/04/22 Javascript
原生js实现自定义难度的扫雷游戏
2021/01/22 Javascript
Python里隐藏的“禅”
2014/06/16 Python
Python的ORM框架中SQLAlchemy库的查询操作的教程
2015/04/25 Python
Python中Iterator迭代器的使用杂谈
2016/06/20 Python
django实现用户登陆功能详解
2017/12/11 Python
Python遍历某目录下的所有文件夹与文件路径
2018/03/15 Python
如何用Python破解wifi密码过程详解
2019/07/12 Python
用Python抢火车票的简单小程序实现解析
2019/08/14 Python
python网络编程:socketserver的基本使用方法实例分析
2020/04/09 Python
英国Zoro工具:手动工具,电动工具和个人防护用品
2016/11/02 全球购物
美国彩妆品牌:Coastal Scents
2017/04/01 全球购物
员工工作表扬信范文
2014/01/13 职场文书
《桃花心木》教学反思
2014/02/17 职场文书
建筑工程材料员岗位职责
2015/04/11 职场文书
解决Pytorch修改预训练模型时遇到key不匹配的情况
2021/06/05 Python
Python利用capstone实现反汇编
2022/04/06 Python
Python 视频画质增强
2022/04/28 Python