文章目录

  • 进制介绍
  • 进制转换
    • parseInt(str, radix)
    • Number()
    • +(一元运算符)
    • Number.prototype.toString(radix)
  • 自定义转换
    • 十进制与十六进制转换
    • 十进制和二进制转换
      • 十进制数字转换成二进制
      • 0.1+ 0.2 != 0.3
      • 二进制数字转换成十进制

进制介绍

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默认的数字进制以外,其他三种进制方式平时使用较少,主要在处理Blob数据、字节编码或者位运算、转义字符等等时候才会碰到。

进制转换

下文将主要讨论进制转换时的问题。
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') //51Number('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 hexwhile (num) {hex = HEXS.charAt(num % 16) + hexnum = Math.floor(num / 16)}return hex
}
function hex2Int (hex = '') {if (typeof hex !== 'string' || hex === '') {return NaN}const hexs = [...hex.toLowerCase()]let resInt = 0for (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 × 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 - numIntegerlet integers = []if (numInteger === 0) {integers = ['0']} else {let integerVal = numIntegerwhile(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 = 49while (decimalVal !== 1 && count > 0) {decimalVal = decimalVal * 2if (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 才能变为整数)。

二进制数字转换成十进制

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

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

  • 小数部分:如 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 = 0if (binDecStr) {binDecimal = [...binDecStr].reduce((res, val, index) => {res += Number(val) * (2 ** (-(index + 1)))return res}, 0)}return parseInt(binIntStr, 2) + binDecimal
    }
    

搞懂JavaScript中的进制与进制转换相关推荐

  1. 来一轮带注释的demo,彻底搞懂javascript中的replace函数

    javascript这门语言一直就像一位带着面纱的美女,总是看不清,摸不透,一直专注服务器端,也从来没有特别重视过,直到最近几年,javascript越来越重要,越来越通用.最近和前端走的比较近,借此 ...

  2. 彻底搞懂javascript中的replace函数

    javascript这门语言一直就像一位带着面纱的美女,总是看不清,摸不透,一直专注服务器端,也从来没有特别重视过,直到最近几年,javascript越来越重要,越来越通用.最近和前端走的比较近,借此 ...

  3. $.ligerdialog.open中确定按钮加事件_彻底搞懂JavaScript中的this指向问题

    JavaScript中的this是让很多开发者头疼的地方,而this关键字又是一个非常重要的语法点.毫不夸张地说,不理解它的含义,大部分开发任务都无法完成. 想要理解this,你可以先记住以下两点: ...

  4. 针对还没搞懂javascript中this关键字的同学

    本篇文章主要针对搞不清this指向的的同学们!不定期更新文章都是我学习过程中积累下的经验,还请大家多多关注我的文章以帮助更多的同学,不对的地方还望留言支持改进! 首先,必须搞清楚在JS里面,函数的几种 ...

  5. proto文件支持继承吗_搞懂 Javascript中this 指向及继承原理

    在理解继承之前,需要知道 js 的三个东西: 什么是 JS 原型链 this 的值到底是什么 JS 的new 到底是干什么的 一.什么是 JS 原型链? 我们知道 JS 有对象,比如 var obj ...

  6. 彻底搞懂javascript中的match, exec的区别

    在工作中经常发现一些同学把这两个方法搞混,以致把自己弄的很郁闷.所以我和大家一起来探讨一下这两个方法的奥妙之处吧. 我们分以下几点来讲解: 相同点: 1.两个方法都是查找符合条件的匹配项,并以数组形式 ...

  7. 五个小例子教你搞懂 JavaScript 作用域问题

    原文:五个小例子教你搞懂 JavaScript 作用域问题 众所周知,JavaScript 的作用域和其他传统语言(类C)差别比较大,掌握并熟练运用JavaScript 的作用域知识,不仅有利于我们阅 ...

  8. 彻底搞懂 JS 中 this 机制

    彻底搞懂 JS 中 this 机制 摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.com/jasonGeng88/blog 目录 this 是什么 this 的四种绑定规 ...

  9. 帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)

    帮你彻底搞懂JS中的prototype.__proto__与constructor(图解) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文 ...

最新文章

  1. 制药企业正在基于AI与基因测序发现真菌更多药用价值
  2. Linux Shell编程基础
  3. Oracle中常用的命令,随着学习进度总结
  4. 软件测试-PR在运行场景的时候报错测试机:localhost,连接失败!请检查执行器与压力机之间的网络连接,以及场景组中各个项目配置的路径
  5. c++STL容器的string
  6. 原生JS封装自己的AJAX
  7. ugui源码_UGUI整体解决方案基础篇(Unity 2019)
  8. 手把手教你建立用户画像和用户场景
  9. java后端简历项目经历_为了面试字节跳动后端开发岗(Java)鬼知道我经历了什么.....
  10. 【Spring】Spring 关于 Redis 的序列化器
  11. linux BufferedImage.createGraphics()卡住不动
  12. php 查询条件 session,PHP获取不到SESSION信息之一般情况解决方案
  13. 公安部计算机信息安全产品质量监督检验中心的质量责任和权限
  14. Python绘图新冠病毒COVID-19
  15. 网页设计图片向上浮动_CSS实现图片向上浮动
  16. Matlab安装过程
  17. 百度ai 性别_是的,ai可能是种族主义者和性别歧视者,所以我们该怎么办
  18. 什么是RST包,什么是三次握手,什么是四次握手 ---请进
  19. 想要创业,却没货源?答应我,别只在阿里妈妈上找了好吗
  20. 市场复苏下:报复性消费,了解一下~

热门文章

  1. ant design vue日历增加小圆点
  2. 云队友丨苏宁30年,只把2件事做到极致
  3. 兰州理工 计算机专业,兰州理工大学计算机与通信学院
  4. fastips和nanoips哪个响应更快 Nano IPS和Fast IPS面板优缺点区别
  5. 小学语文生字表2490字(带拼音)
  6. 学计算机对眼睛伤害大吗,上班族长期面对电脑,对眼部危害有多大,看看这3点你就明白了...
  7. Linux学习之常用命令
  8. Planner Studio Pro Mac版(日历任务管理器)
  9. KDDCUP99数据集处理(Keras)
  10. 判断三角形的类型,是何种三角形(等腰,等边,直角)