簡體   English   中英

如何將 BigInt 轉換為 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

二進制補碼具有以下性質:

  1. 你有一個數字,比如123 ,它是 8 位二進制的01111011 ,你想要它的負數,即-123

  2. 二補說:你想要的答案,把它當作一個正數,和原來的數字123相加,你就會得到全0的 8 位數字溢出。

  3. 例如,將所有內容視為正數, 123 + theAnswerYouWant01111011 + 10000101 ,這恰好是00000000溢出,即100000000 (注意前面的額外1 )。 換句話說,您想要256 - 123 ,即133 ,如果您將133渲染為 8 位,這就是您想要的答案。

  4. 因此,您可以使用 2 8減去原始數,並將其視為正數並使用您已有的.toString(2)顯示它。

  5. 以下是 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 位,則顯示的數字是從1111111110000000的任何位置,並且始終是 8 位。

更多細節:

  1. 您可能已經知道一個的補碼只是簡單地翻轉位(從 0 到 1,以及從 1 到 0)。 另一種思考方式是,將兩個數字相加,它將變為全1
  2. 描述二進制補碼的常用方法是翻轉位,然后將其加1 你看,如果你從11111111開始減去01111011 (十進制的 123),你會得到10000100 ,它與翻轉位完全相同。 (實際上這是從上面得出的:將它們相加得到所有1 ,因此使用所有1減去其中一個得到另一個。
  3. 好吧,所以如果你從11111111開始減去那個數字,然后加上1 ,這和使用11111111 ,加上1並減去那個數字不一樣嗎? 嗯, 111111111100000000 (注意前面額外的1 )——正好從 2 n開始,其中n是 n 位 integer,然后減去那個數字。 所以你明白為什么這篇文章開頭的屬性是真的。
  4. 事實上,二的補碼就是這樣設計的:如果我們想找出2 - 1 ,讓計算機計算出來,我們只需要將這個“二的補碼”視為正數,並使用處理器的“加法”將它們相加即可。電路”: 00000010加上11111111 我們得到00000001但有進位(溢出)。 如果我們通過丟棄它來正確處理溢出,我們會得到答案: 1 如果我們使用反碼,我們不能使用相同的加法電路來執行00000010 + 11111110得到1 ,因為結果是00000000 ,即0
  5. 考慮(4)的另一種方法是,如果你有一輛汽車的里程表,它顯示到目前為止000002英里,你如何從中減去1 好吧,如果您將-1表示為9999999 ,那么您只需將999999添加到2 ,並得到1000001但最左邊的1不會顯示在里程表上,現在里程表將變為000001 在十進制中,將-1表示為999999是 10 的補碼。 在二進制中,將-1表示為11111111稱為二進制補碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM