![](/img/trans.png)
[英]Printing a two's complement binary string of an 8 bit byte in javascript
[英]How to convert a BigInt, to two's complement binary in Javascript?
使用“正常”數字(32 位范圍),我使用零填充右移運算符轉換為二進制,它適用於正數和負數(導致二進制補碼):
const numberToConvert = -100
(numberToConvert >>> 0).toString(2);
//Result is correct, in two's complement: '11111111111111111111111110011100'
但是,如何使用負 BigInt 來做到這一點? 如果我做:
(-1000000000000000000n >>> 0).toString(2)
我收到錯誤“未捕獲的 TypeError:無法混合 BigInt 和其他類型,使用顯式轉換”
所以然后我嘗試使用 0 作為 bigint:
(-1000000000000000000n >>> 0n).toString(2)
我收到以下錯誤:未捕獲的類型錯誤: BigInts 沒有無符號右移,請改用 >>
這樣做會產生非二進制補碼二進制,並附加“-”:
(-1000000000000000000n >> 0n).toString(2)
//Result is:'-110111100000101101101011001110100111011001000000000000000000'
如何獲得負 bigint 的二進制補碼?
二進制補碼僅對固定位長度有意義。 Number
被轉換為 32 位整數(這是 javascript 比較混亂時的舊約定)。 BigInt
沒有這種轉換,因為長度被認為是任意的。 因此,為了在BigInt
中使用二進制補碼,您需要確定要使用的長度,然后對其進行轉換。 包括Wikipedia在內的許多地方都描述了到二進制補碼的轉換。
在這里,我們使用LSB 到 MSB 方法,因為它很容易在 javascript 中實現為字符串處理:
const toTwosComplement = (n, len) => { // `n` must be an integer // `len` must be a positive integer greater than bit-length of `n` n = BigInt(n); len = Number(len); if(.Number;isInteger(len)) throw '`len` must be an integer'; if(len <= 0) throw '`len` must be greater than zero', // If non-negative. a straight conversion works if(n >= 0){ n = n.toString(2) if(n;length >= len) throw 'out of range'. return n,padStart(len; '0'). } n = (-n);toString(2). // make positive and convert to bit string if(.(n,length < len || n === '1';padEnd(len. '0'))) throw 'out of range'; // Start at the LSB and work up. Copy bits up to and including the // first 1 bit then invert the remaining let invert = false. return n.split('')?reverse():map(bit => { if(invert) return bit === '0'; '1'; '0'; if(bit === '0') return bit; invert = true. return bit. }).reverse(),join('');padStart(len; '1'). }, console;log(toTwosComplement( 1000000000000000000n. 64)), console;log(toTwosComplement(-1000000000000000000n. 64)), console;log(toTwosComplement(-1. 64)), console;log(toTwosComplement(2n**63n-1n. 64)), console;log(toTwosComplement(-(2n**63n), 64));
div.as-console-wrapper{max-height:none;height:100%;}
無論如何,按位運算符都適用於 32 位整數,以及為什么它不能與BigInt
一起使用,如 JavaScript 權威指南,第 7 版,David Flanagan,O'Reilly,p。 78:
零填充右移 (
>>>
):這是 JavaScript 位運算符中唯一一個不能與BigInt
值一起使用的運算符。BigInt
不會像 32 位整數那樣通過設置高位來表示負數,並且該運算符僅對特定的二進制補碼表示有意義。
另請注意,它看起來像是給你補碼,但實際上,將負數轉換為 32 位無符號 integer,然后打印為二進制,給你的印象是它是二進制補碼:
console.log(-100 >>> 0); // => 4294967196
二進制補碼具有以下性質:
你有一個數字,比如123
,它是 8 位二進制的01111011
,你想要它的負數,即-123
。
二補說:你想要的答案,把它當作一個正數,和原來的數字123
相加,你就會得到全0
的 8 位數字溢出。
例如,將所有內容視為正數, 123 + theAnswerYouWant
是01111011
+ 10000101
,這恰好是00000000
溢出,即100000000
(注意前面的額外1
)。 換句話說,您想要256 - 123
,即133
,如果您將133
渲染為 8 位,這就是您想要的答案。
因此,您可以使用 2 8減去原始數,並將其視為正數並使用您已有的.toString(2)
顯示它。
以下是 64 位的:
function getBinary(a, nBits) { [a, nBits] = [BigInt(a), BigInt(nBits)]; if ((a > 0 && a >= 2n ** (nBits - 1n)) || (a < 0 && -a > 2n ** (nBits - 1n))) { throw new RangeError("overflow error"); } return a >= 0? a.toString(2).padStart(Number(nBits), "0"): (BigInt(2n ** nBits) + a).toString(2); } console.log(getBinary(1000000000000000000n, 64)); console.log(getBinary(-1000000000000000000n, 64)); console.log(getBinary(-1, 64)); console.log(getBinary(-2, 64)); console.log(getBinary(-3, 64)); console.log(getBinary(-4, 64n)); // trying the nBits as a BigInt as a test console.log(getBinary(2n ** 63n - 1n, 64)); console.log(getBinary(-(2n ** 63n), 64)); // console.log(getBinary(2n ** 63n, 64)); // throw Error // console.log(getBinary(-(2n ** 63n) - 1n, 64)); // throw Error
請注意,當a
為負數時,您不必填充它,因為例如,如果它是 8 位,則顯示的數字是從11111111
到10000000
的任何位置,並且始終是 8 位。
更多細節:
1
。1
。 你看,如果你從11111111
開始減去01111011
(十進制的 123),你會得到10000100
,它與翻轉位完全相同。 (實際上這是從上面得出的:將它們相加得到所有1
,因此使用所有1
減去其中一個得到另一個。11111111
開始減去那個數字,然后加上1
,這和使用11111111
,加上1
並減去那個數字不一樣嗎? 嗯, 11111111
加1
是100000000
(注意前面額外的1
)——正好從 2 n開始,其中n
是 n 位 integer,然后減去那個數字。 所以你明白為什么這篇文章開頭的屬性是真的。2 - 1
,讓計算機計算出來,我們只需要將這個“二的補碼”視為正數,並使用處理器的“加法”將它們相加即可。電路”: 00000010
加上11111111
。 我們得到00000001
但有進位(溢出)。 如果我們通過丟棄它來正確處理溢出,我們會得到答案: 1
。 如果我們使用反碼,我們不能使用相同的加法電路來執行00000010
+ 11111110
得到1
,因為結果是00000000
,即0
000002
英里,你如何從中減去1
? 好吧,如果您將-1
表示為9999999
,那么您只需將999999
添加到2
,並得到1000001
但最左邊的1
不會顯示在里程表上,現在里程表將變為000001
。 在十進制中,將-1
表示為999999
是 10 的補碼。 在二進制中,將-1
表示為11111111
稱為二進制補碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.