JavaScript での複数の塩基と塩基の変換

導入

JavaScript では、10 進数、2 進数、16 進数、および 8 進数の 4 つの表現方法が提供されます。
数値リテラルの場合、主に以下を区別するために異なる接頭辞が使用されます。

  • 10 進数 (10 進数):
    値は数値であり 0-9、接頭辞は使用されません。
  • バイナリ (Binary):
    値はデジタル 0 および 1 ; プレフィックス 0b は または です 0B
  • 16 進数 (16 進数):
    値の数字 0-9 と a-f ; プレフィックス 0x または 0X
  • 8 進数:
    値の数値 0-7 、接頭辞 0o または 0O (ES6 規制)。

非厳密モードでのブラウザのサポートに注意してください: プレフィックス 0 があり、 0-7 その後ろに 8 桁のみが使用されている場合、値は 8 進数とみなされますが、プレフィックス 0 に続く数字に 8 または 9 がある場合は、その後、10 進数として扱われます。
厳密モードでは、数値の先頭に 0 が付いている場合、「Uncaught SyntaxError: 先行ゼロのある小数は厳密モードでは許可されません」というエラーが報告されます。
さまざまな基数の値。値が指定された範囲を超える場合、次のエラーが報告されます: Uncaught SyntaxError: 無効または予期しないトークンです。

JavaScript 内部のデフォルトでは、2 進数、16 進数、および 8 進数のリテラル値は計算のために自動的に 10 進数に変換されます。

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

JavaScript のデフォルトの数値基数が 10 進数であることを除けば、他の 3 つの基数は、主に基礎となるデータ、バイト エンコーディング、またはビット演算を処理する場合に使用されることはほとんどありません。

変換

この記事では主に基数変換の問題点について説明します。
JavaScript は、10 進数と他の基数の間で変換するネイティブ関数を提供します。
このうち、他の基数から 10 進数に変換するには、parseInt()Number()+(単項演算子) の 3 つの方法があります。3 つのメソッドはいずれも整数のみを変換できます。
10 進数から他の基数への変換が使用できます Number.prototype.toString()小数がサポートされています。

parseInt(str, radix)

最初のパラメータは解析される文字列であり、他の塩基には接頭辞は付けられません。
2 番目のパラメータは基数で、変換中に文字列を理解するためにどの基数系が使用されるかを示します。デフォルト値は 10 で、これは 10 進数に変換することを意味します。
2 番目のパラメータが数値でない場合は、自動的に数値に変換されます。数値に変換できない場合、このパラメータは無視されます。数値の場合は、整数である必要があります。この範囲を超える場合は 2-36 、 、返品されます NaN

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

2 番目のパラメーターが渡されない場合、文字列はデフォルトで 10 進数で解析され parseInt ます が0x 、文字列が で始まる場合は 16 進数とみなされます。
また、他の基数の文字列 は、0o21(八进制)その0b11(二进制) 基数で自動変換されずに取得されます 0
したがって、 parseInt バイナリ変換を使用する場合、実行結果の正確性と安定性を確保するために、第 2 パラメータを省略することはできません

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

番号()

文字列を数値に変換したり、他の基数の文字列をサポートしたり、デフォルトで 10 進数に変換したりできます。
文字列に無効な基本文字が含まれているかどうかを返します NaN
基本接頭辞 , 0b,を0o使用する必要があることに注意してください0x

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

Number('0x88kk') // NaN

+ (単項演算子)

と同様 Number() に、文字列を数値に変換したり、他の基数の文字列をサポートしたり、デフォルトで 10 進数に変換したりできます。
文字列に無効な基本文字が含まれているかどうかを返します 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

カスタム変換

これらのネイティブ関数に加えて、基数間の変換関数を自分で実装することもできます。
対応する規則に従って、10 進数、2 進数、および 16 進数間の変換方法をいくつか実現できます。

10 進数から 16 進数への変換

次のコードは、10 進数と 16 進数の間で整数を変換するためのもので、変換は基本的な規則に従って実行されます。
16 進数は  数値を記述する方法であり、 0-9数値 自体の値が取得され、  数値の値が 取得されます 。また、文字は大文字と小文字が区別されません。a-f0-9a-f10-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
}

8 進数を変換する場合、実際には 16 進数と非常に似ており、8 進数の値の範囲に従っていくつかの変更を加えるだけで済みます。8 進数は通常、ほとんど使用されないため、個別にリストされません。

以下では、バイナリ表現や小数の変換など、バイナリ変換の関連知識に焦点を当てます。

10進数と2進数の変換

「10 進数から 2 進数への変換」では、10 進数が 2 進数の間でどのように変換されるかを理解するために、10 進数について考えます。
まず、次のような数値を選択します11.125 。数値のバイナリ表現を見てみましょう。

(11.125).toString(2) // 1011.001

11.125 のバイナリ表現は次のとおりであることがわかります1011.001以下では、この数値を例として変換操作を実行します。

10進数を2進数に変換する

最初に理解する必要があるのは、2 進 10 進表現方法がどのように導出されるのかということです。

  • 整数 部分は 2 進数表現で次のように計算できます。数値 11:
    11 / 2 ——— 1
    5/2 ——— 1
    2 / 2 ——— 0
    1 / 2 ———— 1
    整数の法則その結果、 从下往上逆行は 2 進 1011 数で 11 になります。

  • 小数は 2 進数、10 進数で計算できます 0.125
    たとえば、10 進数 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

    整数 + 10 進数なので、 11.125 バイナリ表現は次のようになります1011.001
    上記の整数と小数の別々の計算の規則に従って、10 進数を 2 進数に変換する関数は次のように得られます。

    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) : '')
    }
    

    10 進数を 2 進数に変換すると、無限ループの問題が発生するため、上記のコードは最初の 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
.. .
...
無限ループ

0.1 1 が得られないので、有限 10 進数を無限 2 進数 に変換し 0.00011001100...0.2 変換すること がわかります 0.001100110011...
無限ループのため、必然的に精度が失われますが、 0.1 + 0.2 精度が失われた後に計算された数値の最後の桁が 0 ではない場合があり、結果は次のようになります0.30000000000000004
精度を切り捨てた後の最後の桁が 0 であれば、当然不等な結果は生じません。たとえば、実際には 0.1 + 0.6 === 0.70.1 と 0.6 はバイナリに変換すると精度が失われますが、切り捨てられた値はすべて 0 なので、等しい。
不平等などもあります 0.1 + 0.7 !== 0.8
したがって、計算中にバイナリに変換するときに精度が失われることが原因です 0.1 + 0.2 !== 0.3

JavaScript のすべての値は、IEEE-754 標準の 64 ビット倍精度浮動小数点数として保存されます。
IEEE 754 標準の 64 ビット倍精度浮動小数点数の小数部は、最大 53 桁の 2 進数をサポートします。
浮動小数点数の小数点以下の桁数の制限により、2 進数はまず切り捨てられてから 10 進数に変換される必要があるため、算術計算を実行するとエラーが発生します。

ここで、10 進数を有限の 2 進数に変換する場合、計算された 10 進数の最初の桁が最後になければならないことがわかります (整数に変換できるのは 10 進数のみであるため 5 )  0.5 × 2 。

2進数を10進数に変換する

その方法は、バイナリを整数部分と小数部分に分割し、それらを別々に変換し、それらを組み合わせて結果の 10 進数値を生成することです。

  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)|1x(2-3) ビット
    ごとの積の結果|0|0|0.125

    最終結果は、ビットごとの積結果の合計です: 0+0+0.125 = 0.125

整数と小数を組み合わせて 1011.001 10 進数を取得します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
}

おすすめ

転載: blog.csdn.net/jh035/article/details/128128178