进制介绍

JavaScript 中提供的进制表示方法有四种:十进制、二进制、十六进制、八进制。

对于数值字面量,主要使用不同的前缀来区分:

  • 十进制(Decimal):

    取值数字 0-9;不用前缀。
  • 二进制(Binary):

    取值数字 01 ;前缀 0b0B
  • 十六进制(Hexadecimal):

    取值数字 0-9a-f ;前缀 0x0X
  • 八进制(Octal):

    取值数字 0-7 ;前缀 0o0O (ES6规定)。

需要注意的是,非严格模式下浏览器支持:如果有前缀0并且后面只用到 0-7 八个数字的数值时,该数值视为八进制;但如果前缀0后面跟随的数字中有8或者9,则视为十进制。

严格模式下,如果数字加前缀0,则报错:Uncaught SyntaxError: Decimals with leading zeros are not allowed in strict mode。

各进制的数值,如果取值数字超过给定的范围,则会报错:Uncaught SyntaxError: Invalid or unexpected token。

在JavaScript内部的默认情况下,二进制、十六进制、八进制字面量数值,都会自动转为十进制进行运算。

0x22 // 34
0b111 // 7
0o33 // 27
0x22 + 0b111 // 41
0o33 + 12 // 39
(0x33).toString() // 51
(0x33).valueOf() // 51

除了十进制是Javascript默认的数字进制以外,其他三种进制方式平时使用较少,主要在处理底层数据、字节编码或者位运算等时候才会碰到。

进制转换

本文将主要讨论进制转换时的问题。

JavaScript 提供了原生函数,进行十进制与其他各进制之间的相互转换。

其中,从其他进制转换成十进制,有三种方式:parseInt()Number()+(一元运算符)。这三种方式都只能转换整数。

从十进制转换成其他进制,可以使用 Number.prototype.toString()。支持小数。

parseInt(str, radix)

第一个参数是需要解析的字符串;其他进制不加前缀。

第二个参数是一个进制基数,表示转换时按什么进制来理解这个字符串,默认值10,表示转十进制。

第二个参数如果非数字,则自动转数字,如无法转称数字则忽略该参数;是数字时,必须是 2-36 的整数,超出该范围,返回 NaN

parseInt('1111', 2) // 15
parseInt('1234', 8) // 668
parseInt('18af', 16) // 6319
parseInt('1111') // 1111

如果不传入第二参数,则 parseInt 会默认使用十进制来解析字符串;但是,如果字符串以 0x 开头,会被认为是十六进制数。

而其他进制的字符串,0o21(八进制)0b11(二进制) 不会以该进制基数自动转换,而是得到 0

所以,在使用 parseInt 进行进制转换时,为了保证运行结果的正确性和稳定性,第二个参数不能省略

parseInt('0x21') // 33
parseInt('0o21') // 0
parseInt('0b11') // 0
parseInt('111', 'add') // 111
parseInt('111', '787') // NaN

如果需要解析的字符串中存在对于当前进制基数无效的字符,则会从最高位取有效字符进行转换,没有效字符则返回NaN。

parseInt('88kk', 16) // 136,=== 0x88
parseInt('kk', 16) // NaN

Number()

可以把字符串转为数字,支持其他进制的字符串,默认转成十进制数字。

字符串中如果存在无效的进制字符时,返回 NaN

记住,需要使用进制前缀,0b0o0x

Number('0b11100') // 28
Number('0o33') // 27
Number('0x33') //51 Number('0x88kk') // NaN

+(一元运算符)

Number() 一样,可以把字符串转为数字,支持其他进制的字符串,默认转成十进制数字。

字符串中如果存在无效的进制字符时,返回 NaN

也需要使用进制前缀。

+'0b11100' // 28
+'0o33' // 27
+'0x33' //51 +'0x88kk' // NaN

可以看到,基本和 Number() 是一样的,都在本质上是对数字的一种转换处理。

Number.prototype.toString(radix)

它支持传入一个进制基数,用于将数字转换成对应进制的字符串,它支持转换小数

未指定默认值为 10,基数参数的范围 2-36,超过范围,报错:RangeError。

15..toString(2) // 1111
585..toString(8) // 1111
4369..toString(16) // 1111
(11.25).toString(2) // 1011.01

自定义转换

除了这些原生函数以外,也可以自己实现进制数字之间的转换函数。

根据相应的规则,就可以实现十进制与二进制、十六进制之间的转换的一些方法。

十进制与十六进制转换

以下代码是针对整数在十进制与十六进制之间的转换,根据基本规则进行换算。

十六进制是以 0-9a-f 进行描述数字的一种方式,其中 0-9 取本身数字的值,而 a-f 则取 10-15 的值。

且字母不区分大小写。

function int2Hex (num = 0) {
if (num === 0) {
return '0'
}
const HEXS = '0123456789abcdef'
let hex
while (num) {
hex = HEXS.charAt(num % 16) + hex
num = Math.floor(num / 16)
}
return hex
}
function hex2Int (hex = '') {
if (typeof hex !== 'string' || hex === '') {
return NaN
}
const hexs = [...hex.toLowerCase()]
let resInt = 0
for (let i = 0; i < hexs.length; i++) {
const hv = hexs[i]
let num = hv.charCodeAt() < 58 ? +hv : ((code - 97) + 10)
resInt = resInt * 16 + num
}
return resInt
}

如果要转换八进制,实际上与十六进制很类似,只需根据八进制的数值范围进行部分改动即可。八进制一般使用非常少,不单独列出。

下面将重点介绍二进制转换的相关知识,包括小数的二进制表示与转换。

十进制和二进制转换

在十进制与二进制的转换中,我们将考虑小数,理解小数是如何在这两者之间进行转换。

先选定一个数字,比如:11.125 ,我们看下该数字在二进制里的表示:

(11.125).toString(2) // 1011.001

可以看到,11.125 的二进制表示为:1011.001。下面将以这个数字为例进行转换操作。

十进制数字转换成二进制

首先需要了解的是,二进制小数表示方法是如何得来的:

  • 整数 部分,用二进制表示可以如此计算,数字 11:

    11 / 2 ———— 1

    5 / 2 ———— 1

    2 / 2 ———— 0

    1 / 2 ———— 1

    整数部分的规则,得到的结果是 从下往上,倒着排 1011 就是二进制的 11。

  • 小数 用二进制表示可以如此计算,小数 0.125

    例如十进制的 0.125

    0.125 × 2 = 0.25 ———— 0

    0.25 × 2 = 0.5 ———— 0

    0.5 × 2 = 1 ———— 1

    只有等于1时才结束,如果结果不等于1将会一直循环下去。

    小数部分的规则,得到的结果是 从上往下,顺着排 0.001 就是二进制的 0.125

    整数 + 小数,所以 11.125 的二进制表示方式:1011.001

    根据以上整数和小数分开计算的规则,就可以得出十进制转二进制的函数,如下:

    function c10to2 (num) {
    // 整数
    const numInteger = Math.floor(num)
    // 小数
    const numDecimal = num - numInteger let integers = []
    if (numInteger === 0) {
    integers = ['0']
    } else {
    let integerVal = numInteger
    while(integerVal !== 1) {
    integers.push(integerVal % 2 === 0 ? '0' : '1')
    integerVal = Math.floor(integerVal / 2)
    }
    integers.push('1')
    }
    const resInteger = integers.reverse().join('') let decimals = []
    if (numDecimal) {
    let decimalVal = numDecimal
    // 最多取49位的长度
    let count = 49
    while (decimalVal !== 1 && count > 0) {
    decimalVal = decimalVal * 2
    if (decimalVal >= 1) {
    decimals.push('1')
    if (decimalVal > 1) {
    decimalVal = decimalVal - 1
    }
    } else {
    decimals.push('0')
    }
    count--
    }
    }
    const resDecimal = decimals.join('') return resInteger + (resDecimal ? ('.' + resDecimal) : '')
    }

    小数在转换成二进制时,会存在无限循环的问题,上面的代码里截取了前49个值。

    所以,这里就会引出了一个问题,就是常见的一个数字精度问题:0.1 + 0.2 != 0.3

0.1+ 0.2 != 0.3

直接看一下 0.1 转二进制:

0.1 × 2 = 0.2

0.2 × 2 = 0.4

0.4 × 2 = 0.8

0.8 × 2 = 1.6

0.6 × 2 = 1.2

0.2 × 2 = 0.4 // 循环开始

0.4 × 2 = 0.8

0.8 × 2 = 1.6

0.6 × 2 = 1.2

...

...

无限循环

0.2 转二进制:

0.2 × 2 = 0.4

0.4 × 2 = 0.8

0.8 × 2 = 1.6

0.6 × 2 = 1.2

0.2 × 2 = 0.4 // 循环开始

0.4 × 2 = 0.8

0.8 × 2 = 1.6

0.6 × 2 = 1.2

...

...

无限循环

因为无法得到1,可以发现有限十进制小数, 0.1 转换成了无限二进制小数 0.00011001100...0.2 转成了 0.001100110011...

由于无限循环,必然会导致精度丢失,正好 0.1 + 0.2 计算得到的数字在丢失精度后的最后一位不为0,所以导致结果为:0.30000000000000004

如果截取精度后最后一位为0,那自然就不存在结果不相等的情况,如 0.1 + 0.6 === 0.7,事实上,0.1和0.6转二进制后都会丢失精度,但截取到的数值都是0,所以相等。

同样不相等的还设有 0.1 + 0.7 !== 0.8等等。

所以是计算时转二进制的精度丢失,才导致的 0.1 + 0.2 !== 0.3

在 JavaScript 中所有数值都以 IEEE-754 标准的 64 bit 双精度浮点数进行存储的。

IEEE 754 标准的 64 位双精度浮点数的小数部分最多支持53位二进制位。

因浮点数小数位的限制而需要先截断二进制数字,再转换为十进制,所以在进行算术计算时会产生误差。

这里能看到,如果十进制小数要被转化为有限二进制小数,那么它计算后的小数第一位数必然要是 5 结尾才行(因为只有 0.5 × 2 才能变为整数)。

二进制数字转换成十进制

方法是:将二进制分成整数和小数两部分,分别进行转换,然后再组合成结果的十进制数值。

  1. 整数部分:这里直接使用 parseInt 函数,parseInt('1011', 2) => 11

  2. 小数部分:如 1011.001 的小数位 001,使用下表的计算方式。

    小数部分|0|0|1

    --|--|--|--

    基数的位数次幂|2-1|2-2|2^-3

    每位与基数乘积|0 × (2^-1)|0 × (2-2)|1×(2-3)

    每位乘积结果|0|0|0.125

    最后的结果是每位乘积结果相加:0+0+0.125 = 0.125

整数与小数合起来,就得到了 1011.001 的十进制数字:11.125

根据规则,代码实现如下所示:

function c2To10 (binaryStr = '') {
if (typeof binaryStr !== 'string' || binaryStr === '') {
return NaN
}
const [ binIntStr, binDecStr ] = binaryStr.split('.')
let binDecimal = 0
if (binDecStr) {
binDecimal = [...binDecStr].reduce((res, val, index) => {
res += Number(val) * (2 ** (-(index + 1)))
return res
}, 0)
}
return parseInt(binIntStr, 2) + binDecimal
}

JavaScript中的多种进制与进制转换的更多相关文章

  1. javascript中的Array对象 —— 数组的合并、转换、迭代、排序、堆栈

    Array 是javascript中经常用到的数据类型.javascript 的数组其他语言中数组的最大的区别是其每个数组项都可以保存任何类型的数据.本文主要讨论javascript中数组的声明.转换 ...

  2. javascript 中数组的创建 添加 与将数组转换成字符串 页面三种提交请求的方式

    创建js数组 var array=new Array(); Java中创建数组 private String[] array=new String[3]; 两个完全不同的,js中是可变长度的 添加内容 ...

  3. 深入剖析JavaScript中的对象与原始值之间的转换机制

    我们都知道原始值之间是可以互相转换的,但是如果对象转原始值呢? 所有的对象在布尔上下文(context)中均为 true .所以对于对象,不存在 to-boolean 转换, 只有字符串和数值转换. ...

  4. javascript中的隐式类型转化

    javascript中的隐式类型转化 #隐式转换 ## "+" 字符串和数字 如果某个操作数是字符串或者能够通过以下步骤转换为字符串的话,+将进行拼接操作. 如果其中一个操作数是对 ...

  5. Javascript中双等号(==)隐性转换机制

    在Javascript中判断相等关系有双等号(==)和三等号(===)两种.其中双等号(==)是值相等,而三等号(===)是严格相等(值及类型是否完全相等). 因此有几个常识知识: 1.对于strin ...

  6. Javascript中双等号(==)隐性转换机制 JS里charCodeAt()和fromCharCode()方法拓展应用:加密与解密

    Javascript中双等号(==)隐性转换机制   在Javascript中判断相等关系有双等号(==)和三等号(===)两种.其中双等号(==)是值相等,而三等号(===)是严格相等(值及类型是否 ...

  7. JavaScript中进制之间的转换

    JavaScript中进制之间的转换 //十进制转其他 var x = 100; alert(x); alert(x.toString(2)); //转2进制 alert(x.toString(8)) ...

  8. JavaScript中进制和字符编码问题

    1.进制: JavaScript中允许使用字面量的形式声明不同进制的数字: var a = 0b10; // 2 声明一个二进制 var b = 010; // 8 八进制,严格模式下会报错 var ...

  9. JavaScript中二进制与10进制互相转换

    webpack打包生成的代码中涉及了一些二进制位与的操作, 所以今天来学习一下JavaScript中的二进制与十进制转换操作吧 十进制转二进制: var num = 100 num.toString( ...

随机推荐

  1. 从零到熟悉,带你掌握Python len() 函数的使用

    摘要:本文为你带来如何找到长度内置数据类型的使用len() 使用len()与第三方数据类型 提供用于支持len()与用户定义的类. 本文分享自华为云社区<在 Python 中使用 len() 函 ...

  2. 异构智联Wi-Fi+蓝牙模组,连接快、准、稳!

    下班回家打开门,电灯.电视.空调.音响.电动窗帘.扫地机器人--一呼百应,有序开工,原本冰冷的房子立刻变成了温暖港湾.可以说,舒适便捷的智能设备已经完全融入了我们的生活中. 从单一场景.单一设备,到现 ...

  3. javascript-jquery对象的其他处理

    一.对元素进行遍历操作 如果要遍历一个jquery对象,对其中每个匹配元素进行相应处理,那么可以使用each()方法. $("div").each(function(index,e ...

  4. Scrum Meeting 0505

    零.说明 日期:2021-5-5 任务:简要汇报两日内已完成任务,计划后两日完成任务 一.进度情况 组员 负责 两日内已完成的任务 后两日计划完成的任务 qsy PM&前端 完成邮箱注册页面功 ...

  5. [no_code]团队贡献分分配规则

    项目 内容 2020春季计算机学院软件工程(罗杰 任健) 2020春季计算机学院软件工程(罗杰 任健) 作业要求 团队贡献分分配规则 我们在这个课程的目标是 远程协同工作,采用最新技术开发软件 这个作 ...

  6. Noip模拟5 2021.6.7

    T1 string(线段树优化) 看到数据范围就必须要想到优化,那么如何把26×M∗N 的复杂度降低呢?? 用到那个我们最不想打的数据结构--线段树...... 然而,这个线段树与往常不同,他只需要用 ...

  7. CF375D Tree and Queries 题解

    感觉CF的题目名都好朴素的样子 你谷链接 首先这题显然是个dsu on tree 但是我不会. 其次这题显然是个莫队.这我会啊! 然后会发现好像不是很对劲.因为每次询问都有一个k,貌似和传统的莫队数颜 ...

  8. 攻防世界 web4.cookie

    题有几种解法,我有点懒,懒的打开burp,所以可以直接在浏览器拿flag, 首先访问ip/cookie.php,提示:See the http response 接着F12查看响应头 给你cyberp ...

  9. AtCoder Regular Contest 128 部分题题解

    关于鄙人罚坐两小时那件事...该开始看A题,这不就是个DP记录路径吗?Wrong了,嗯,我没用double,又Wrong,怎么回事,使劲检查自己的算法和细节问题,一个小时过去了,...这没错啊,又反复 ...

  10. Ubuntu安装数据库

    1.通过命令行安装:sudo apt-get install mysql-client mysql-server 2.安装过程中输入数据库密码("123456",root) 3.使 ...