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"
なぜそれが適切に機能しないのか、またはこのように機能する理由は何ですか?検索しましたが、役立つものは見つかりませんでした。
toString()
関数は、10進数を取り、それを2進数に変換し、「-」記号を追加します。
ゼロフィル右シフトは、そのオペランドを2つの補数形式の符号付き32ビット整数に変換します。
質問1:
_//If you try
(-3).toString(2); //show "-11"
_
それは関数 .toString()
にあります。 .toString()
を介して数値を出力する場合:
構文
numObj.toString([基数])
numObjが負の場合、符号は保持されます。これは、基数が2の場合でも同じです。返される文字列は、numObjの2の補数ではなく、-記号が前に付いたnumObjの正のバイナリ表現です。
10進数を取り、それを2進数に変換し、「-」記号を追加します。
質問2:
_// but if you fake a bit shift operation it works as expected
(-3 >>> 0).toString(2); // print "11111111111111111111111111111101"
_
ゼロフィル右シフトは、そのオペランドを 符号付き32ビット整数 に変換します。その演算の結果は常に符号なし32ビット整数になります。
すべてのビット単位演算子のオペランドは、2の補数形式の符号付き32ビット整数に変換されます。
-3 >>> 0(右論理シフト)は、引数を符号なし整数に強制変換します。これが、-3の32ビットの2の補数表現を取得する理由です。
var binary = (-3 >>> 0).toString(2); // coerced to uint32
console.log(binary);
console.log(parseInt(binary, 2) >> 0); // to int32
オン jsfiddle
出力は
11111111111111111111111111111101
-3
.toString()
は、文字列表現で数値の符号を返すように設計されています。 EcmaScript 2015、セクション7.1.12.1 を参照してください:
- mがゼロより小さい場合、文字列「-」とToString(−m)。
セクション20.1.3.6 から結論付けることができるように、このルールは基数が引数として渡される場合も同じです。
- 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 で指定された評価プロセスから明らかです。
- lnumをToUint32(lval)とします。
ToUint32 演算には、オペランドが符号なし32ビット範囲にマッピングされるステップがあります。
- int32bitをintモジュロ2とする32。
上記のモジュロ演算(JavaScriptの_%
_演算子で confused しないでください))を例の値-3に適用すると、実際に4294967293になります。
-3と4294967293は明らかに同じ数ではないため、_(-3).toString(2)
_が_(4294967293).toString(2)
_と同じでないことは当然のことです。
ここでいくつかのポイントを要約しますが、他の答えが少し混乱している場合:
(-3 >>> 0).toString(2)
_、それをAと呼びましょう。しかし、その理由と仕組みを知りたいvar num = -3; num.toString(-3)
を使用した場合、_-11
_を取得することになります。これは、前に負の符号が付いた数値3の符号なしバイナリ表現であり、必要なものではありません。1)_(-3 >>> 0)
_
_>>>
_演算は、符号付き整数である左のオペランド(-3)を取り、ビットを0の位置に左にシフトする(ビットは変更されない)だけでなく、これらの変更されていないビットに対応する符号なしの数値を返します。
符号付き数値-3のビットシーケンスは、符号なし数値4294967293と同じビットシーケンスです。これは、_-3 >>> 0
_をREPLに単に入力した場合にノードに提供されるものです。
2)_(-3 >>> 0).toString
_
ここで、この符号なし数値でtoString
を呼び出すと、数値のビットの文字列表現が取得されます。これは、-3と同じビットのシーケンスです。
私たちが効果的に行ったことは、「ちょっとtoString、符号なし整数のビットを出力するように指示すると通常の動作になるので、符号付き整数を出力したいので、それを符号なし整数に変換します。そして、あなたは私のためにビットを印刷します。」