web-dev-qa-db-ja.com

JavaScriptで負の数値をバイナリ文字列に

Javascript Number.toString 関数は負の数を正しく表しませんか?

//If you try
(-3).toString(2); //shows "-11"
// but if you fake a bit shift operation it works as expected
(-3 >>> 0).toString(2); // print "11111111111111111111111111111101"

なぜそれが適切に機能しないのか、またはこのように機能する理由は何ですか?検索しましたが、役立つものは見つかりませんでした。

44
fernandosavio

簡潔な答え:

  1. toString()関数は、10進数を取り、それを2進数に変換し、「-」記号を追加します。

  2. ゼロフィル右シフトは、そのオペランドを2つの補数形式の符号付き32ビット整数に変換します。

より詳細な答え:

質問1:

_//If you try
(-3).toString(2); //show "-11"
_

それは関数 .toString() にあります。 .toString()を介して数値を出力する場合:

構文

numObj.toString([基数])

numObjが負の場合、符号は保持されます。これは、基数が2の場合でも同じです。返される文字列は、numObjの2の補数ではなく、-記号が前に付いたnumObjの正のバイナリ表現です。

10進数を取り、それを2進数に変換し、「-」記号を追加します。

  1. 基数2に変換された基数10「3」は「11」
  2. サインを追加すると、「-11」が表示されます

質問2:

_// but if you fake a bit shift operation it works as expected
        (-3 >>> 0).toString(2); // print "11111111111111111111111111111101"
_

ゼロフィル右シフトは、そのオペランドを 符号付き32ビット整数 に変換します。その演算の結果は常に符号なし32ビット整数になります。

すべてのビット単位演算子のオペランドは、2の補数形式の符号付き32ビット整数に変換されます。

16
Daan

-3 >>> 0(右論理シフト)は、引数を符号なし整数に強制変換します。これが、-3の32ビットの2の補数表現を取得する理由です。

http://en.wikipedia.org/wiki/Two%27s_complement

http://en.wikipedia.org/wiki/Logical_shift

27
Steve Wang
var binary = (-3 >>> 0).toString(2); // coerced to uint32

console.log(binary);

console.log(parseInt(binary, 2) >> 0); // to int32

オン jsfiddle

出力は

11111111111111111111111111111101
-3 
18
Xotic750

.toString()は、文字列表現で数値の符号を返すように設計されています。 EcmaScript 2015、セクション7.1.12.1 を参照してください:

  1. mがゼロより小さい場合、文字列「-」とToString(−m)。

セクション20.1.3.6 から結論付けることができるように、このルールは基数が引数として渡される場合も同じです。

  1. radixNumberで指定された基数を使用して、この数値の文字列表現を返します。 [...]アルゴリズムは、7.1.12.1で指定されたものの一般化である必要があります。

それが理解されたら、意外なことは、なぜ_-3 >>> 0_で同じことができないのかということです。

ただし、thatの動作は実際には.toString(2)とは関係ありません。値が呼び出される前はすでに異なるためです。

console.log (-3 >>> 0); // 4294967293

これは、_>>>_演算子の動作の結果です。

(これを書いている時点では) mdn に関する情報が完全に正しくないことも役に立ちません。それは言う:

すべてのビット単位演算子のオペランドは、2の補数形式の符号付き32ビット整数に変換されます。

しかし、これはallビットごとの演算子には当てはまりません。 _>>>_演算子は、ルールの例外です。これは、 EcmaScript 2015、セクション12.5.8.1 で指定された評価プロセスから明らかです。

  1. lnumをToUint32(lval)とします。

ToUint32 演算には、オペランドが符号なし32ビット範囲にマッピングされるステップがあります。

  1. int32bitintモジュロ2とする32

上記のモジュロ演算(JavaScriptの_%_演算子で confused しないでください))を例の値-3に適用すると、実際に4294967293になります。

-3と4294967293は明らかに同じ数ではないため、_(-3).toString(2)_が_(4294967293).toString(2)_と同じでないことは当然のことです。

1
trincot

ここでいくつかのポイントを要約しますが、他の答えが少し混乱している場合:

  • 取得したいのは、バイナリ表現の負の数の文字列表現です。これは、文字列が符号付き2進数を示すことを意味します(2の補数を使用)
  • 式_(-3 >>> 0).toString(2)_、それをAと呼びましょう。しかし、その理由と仕組みを知りたい
  • var num = -3; num.toString(-3)を使用した場合、_-11_を取得することになります。これは、前に負の符号が付いた数値3の符号なしバイナリ表現であり、必要なものではありません。
  • 式Aは次のように機能します。

1)_(-3 >>> 0)_

_>>>_演算は、符号付き整数である左のオペランド(-3)を取り、ビットを0の位置に左にシフトする(ビットは変更されない)だけでなく、これらの変更されていないビットに対応する符号なしの数値を返します。

符号付き数値-3のビットシーケンスは、符号なし数値4294967293と同じビットシーケンスです。これは、_-3 >>> 0_をREPLに単に入力した場合にノードに提供されるものです。

2)_(-3 >>> 0).toString_

ここで、この符号なし数値でtoStringを呼び出すと、数値のビットの文字列表現が取得されます。これは、-3と同じビットのシーケンスです。

私たちが効果的に行ったことは、「ちょっとtoString、符号なし整数のビットを出力するように指示すると通常の動作になるので、符号付き整数を出力したいので、それを符号なし整数に変換します。そして、あなたは私のためにビットを印刷します。」

0
evianpring