«

JavaScript中的进制与进制转换是什么

时间:2024-3-7 10:11     作者:韩俊     分类: Javascript


这篇“JavaScript中的进制与进制转换是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“JavaScript中的进制与进制转换是什么”文章吧。

进制介绍


JavaScript

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


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

  • 十进制(Decimal):取值数字

    0-9
    ;不用前缀。


  • 二进制(Binary):取值数字

    0
    1
    ;前缀
    0b
    0B


  • 十六进制(Hexadecimal):取值数字

    0-9
    a-f
    ;前缀
    0x
    0X


  • 八进制(Octal):取值数字

    0-7
    ;前缀
    0o
    0O
    (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


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

0b

0o

0x


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-9

a-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 &mdash;&mdash;&mdash;&mdash; 1
5 / 2 &mdash;&mdash;&mdash;&mdash; 1
2 / 2 &mdash;&mdash;&mdash;&mdash; 0
1 / 2 &mdash;&mdash;&mdash;&mdash; 1

整数部分的规则,得到的结果是 从下往上,倒着排

1011

就是二进制的

11


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

0.125


0.125 &times; 2 = 0.25 &mdash;&mdash;&mdash;&mdash; 0
0.25 &times; 2 = 0.5 &mdash;&mdash;&mdash;&mdash; 0
0.5 &times; 2 = 1 &mdash;&mdash;&mdash;&mdash; 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 &times; 2 = 0.2
0.2 &times; 2 = 0.4
0.4 &times; 2 = 0.8
0.8 &times; 2 = 1.6
0.6 &times; 2 = 1.2
0.2 &times; 2 = 0.4 // 循环开始
0.4 &times; 2 = 0.8
0.8 &times; 2 = 1.6
0.6 &times; 2 = 1.2
...
...

无限循环


0.2

转二进制:


0.2 &times; 2 = 0.4
0.4 &times; 2 = 0.8
0.8 &times; 2 = 1.6
0.6 &times; 2 = 1.2
0.2 &times; 2 = 0.4 // 循环开始
0.4 &times; 2 = 0.8
0.8 &times; 2 = 1.6
0.6 &times; 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 &times; 2

才能变为整数)。


二进制数字转换成十进制

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

整数部分:这里直接使用

parseInt

函数,

parseInt('1011', 2) => 11


小数部分:如

1011.001

的小数位

001

,使用下表的计算方式。


小数部分 0 0 1
基数的位数次幂 2^-1 2^-2 2^-3
每位与基数乘积 0 &times; (2^-1) 0 &times; (2^-2) 1&times;(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

热门推荐