前置閱讀:html
有符號二進制整數有正數和負數。在 x86 處理器中,MSB 表示的是符號位:0 表示正數,1 表示負數。下圖展現了 8 位的正數和負數:編程
概念總結:segmentfault
表示正數的補碼能夠直接轉成十進制,表示負數的補碼想要轉回十進制步驟以下:安全
反碼能夠經過二進制值按位取反獲得(全部二進制位都取反)
正數的反碼示例:併發
十進制數值 | 補碼 | 反碼 |
---|---|---|
0 | 0000 0000 | 1111 1111 |
1 | 0000 0001 | 1111 1110 |
2 | 0000 0010 | 1111 1101 |
3 | 0000 0011 | 1111 1100 |
4 | 0000 0100 | 1111 1011 |
負數的反碼示例:函數
十進制數值 | 補碼 | 反碼 |
---|---|---|
-0 | 0000 0000 | 1111 1111 |
-1 | 1111 1111 | 0000 0000 |
-2 | 1111 1110 | 0000 0001 |
-3 | 1111 1101 | 0000 0010 |
在計算機內全部數值底層都用補碼錶示,不管正負數(十進制)
十進制數值 | 補碼 |
---|---|
0 | 0000 0000 |
1 | 0000 0001 |
2 | 0000 0010 |
3 | 0000 0011 |
-0 | 0000 0000 |
-1 | 1111 1111 |
-2 | 1111 1110 |
-3 | 1111 1101 |
負數補碼計算過程示例:ui
十進制數值 | 絕對值 | 絕對值補碼 | 絕對值補碼取反 | 絕對值補碼取反加一 | 正確補碼 | 十進制數值 |
---|---|---|---|---|---|---|
-0 | 0 | 0000 0000 | 1111 1111 | 1111 1111 + 1 ————— 1,0000 0000 |
0000 0000 | -0 |
-1 | 1 | 0000 0001 | 1111 1110 | 1111 1110 + 1 ————— 1111 1111 |
1111 1111 | -1 |
-2 | 2 | 0000 0010 | 1111 1101 | 1111 1101 + 1 ————— 1111 1110 |
1111 1110 | -2 |
-3 | 3 | 0000 0011 | 1111 1100 | 1111 1100 + 1 ————— 1111 1101 |
1111 1101 | -3 |
-4 | 4 | 0000 0100 | 1111 1011 | 1111 1011 + 1 ————— 1111 1100 |
1111 1100 | -4 |
-5 | 5 | 0000 0101 | 1111 1010 | 1111 1010 + 1 ————— 1111 1011 |
1111 1011 | -5 |
表示正數的補碼能夠直接轉成十進制,表示負數的補碼想要轉回十進制步驟以下:atom
- 對錶示負數的補碼取反碼加一獲得負數的十進制絕對值補碼;
- 再將負數的十進制絕對值補碼轉成十進制獲得負數的十進制絕對值;
- 最後加上符號位;
MSB | 補碼 | 十進制數值 |
---|---|---|
0 | 0000 0000 | 0 |
0 | 0000 0001 | 1 |
0 | 0000 0010 | 2 |
0 | 0000 0011 | 3 |
0 | 0000 0100 | 4 |
0 | 0000 0101 | 5 |
1 | 1111 1111 | -1 |
1 | 1111 1110 | -2 |
1 | 1111 1101 | -3 |
1 | 1111 1100 | -4 |
1 | 1111 1011 | -5 |
負數轉換示例:spa
MSB | 補碼 | 補碼取反 | 補碼取反加一 | 補碼取反加一後所表明十進制值 | 符號 | 十進制結果 | 補碼 |
---|---|---|---|---|---|---|---|
1 | 1111 1111 | 0000 0000 | 0000 0001 | 1 | - | -1 | 1111 1111 |
1 | 1111 1110 | 0000 0001 | 0000 0010 | 2 | - | -2 | 1111 1110 |
1 | 1111 1101 | 0000 0010 | 0000 0011 | 3 | - | -3 | 1111 1101 |
1 | 1111 1100 | 0000 0011 | 0000 0100 | 4 | - | -4 | 1111 1100 |
1 | 1111 1011 | 0000 0100 | 0000 0101 | 5 | - | -5 | 1111 1011 |
不管是正數加正數(十進制加法)仍是正數/負數加負數(十進制減法)均可以用補碼加補碼錶示
正數加正數的補碼計算過程示例:.net
表達式 | 補碼相加 | 二進制結果 | 十進制結果 |
---|---|---|---|
0+0 | 0000 0000 + 0000 0000 —————— 0000 0000 |
0000 0000 | 0 |
0+1 | 0000 0000 + 0000 0001 —————— 0000 0001 |
0000 0001 | 1 |
1+1 | 0000 0001 + 0000 0001 —————— 0000 0010 |
0000 0010 | 2 |
2+1 | 0000 0010 + 0000 0001 —————— 0000 0011 |
0000 0011 | 3 |
正數加負數的補碼計算過程示例:
表達式 | 補碼相加 | 二進制結果 | 十進制結果 |
---|---|---|---|
0+(-0) | 0000 0000 + 0000 0000 —————— 0000 0000 |
0000 0000 | 0 |
0+(-1) | 0000 0000 + 1111 1111 —————— 1111 1111 |
1111 1111 | -1 |
1+(-1) | 0000 0001 + 1111 1111 —————— 1,0000 0000 |
0000 0000 | 0 |
1+(-2) | 0000 0001 + 1111 1110 —————— 1111 1111 |
1111 1111 | -1 |
2+(-2) | 0000 0010 + 1111 1110 —————— 1,0000 0000 |
0000 0000 | 0 |
2+(-1) | 0000 0010 + 1111 1111 —————— 1,0000 0001 |
0000 0001 | 1 |
負數加負數的補碼計算過程示例:
表達式 | 補碼相加 | 二進制結果 | 十進制結果 |
---|---|---|---|
(-0)+(-0) | 0000 0000 + 0000 0000 —————— 0000 0000 |
0000 0000 | 0 |
(-1)+(-1) | 1111 1111 + 1111 1111 —————— 1,1111 1110 |
1111 1110 | -2 |
(-1)+(-2) | 1111 1111 + 1111 1110 —————— 1,1111,1101 |
1111 1101 | -3 |
一樣的一串二進制數字,便可以是反碼也能夠是補碼,若是是補碼則其能夠經過上述規則轉成對應的十進制數值,若是是反碼則表明其爲計算過程當中間值,若是想知道反碼在十進制中所表示的數值,能夠將其視爲補碼再經過上述規則轉成十進制便可。
正數示例:
十進制數值 x | x 取補碼 fn1(x)=a | x 取反碼 fn2(x)=b | b 的十進制形式 y |
---|---|---|---|
0 | 0000 0000 | 1111 1111 | -1 |
1 | 0000 0001 | 1111 1110 | -2 |
2 | 0000 0010 | 1111 1101 | -3 |
3 | 0000 0011 | 1111 1100 | -4 |
4 | 0000 0100 | 1111 1011 | -5 |
負數示例:
十進制數值 x | x 取補碼 fn1(x)=a | x 取反碼 fn2(x)=b | b 的十進制形式 y |
---|---|---|---|
-0 | 0000 0000 | 1111 1111 | -1 |
-1 | 1111 1111 | 0000 0000 | 0 |
-2 | 1111 1110 | 0000 0001 | 1 |
-3 | 1111 1101 | 0000 0010 | 2 |
示例彙總:
十進制數值 x | x 取補碼 fn1(x)=a | x 取反碼 fn2(x)=b | b 的十進制形式 y | y + 1 | 十進制數值 x |
---|---|---|---|---|---|
0 | 0000 0000 | 1111 1111 | -1 | 0 | 0 |
1 | 0000 0001 | 1111 1110 | -2 | -1 | 1 |
2 | 0000 0010 | 1111 1101 | -3 | -2 | 2 |
3 | 0000 0011 | 1111 1100 | -4 | -3 | 3 |
-0 | 0000 0000 | 1111 1111 | -1 | 0 | -0 |
-1 | 1111 1111 | 0000 0000 | 0 | 1 | -1 |
-2 | 1111 1110 | 0000 0001 | 1 | 2 | -2 |
-3 | 1111 1101 | 0000 0010 | 2 | 3 | -3 |
經過該表格示例能夠得出如下兩個規律:
反碼所表示的數值與原數值之間規律以下(y 表明反碼以後的十進制值):
即若是想獲得一個十進制正數值的負數形式(1 => -1)或則獲得一個十進制負數值的正數形式能夠經過對原值取反碼加一獲得:
十進制數值 x | 十進制取反 -x | 過程 |
---|---|---|
0 | 0 | 取反碼(0)+1 = -1+1 |
1 | -1 | 取反碼(1)+1 = -2+1 |
2 | -2 | 取反碼(2)+1 = -3+1 |
3 | -3 | 取反碼(3)+1 = -4+1 |
-1 | 1 | 取反碼(-1)+1 = 0+1 |
-2 | 2 | 取反碼(-2)+1 = 1+1 |
-3 | 3 | 取反碼(-3)+1 = 2+1 |
將示例彙總表格再進一步簡化:
十進制數值 x | x 的反碼十進制表示形式 y | 翻譯 -1 | 翻譯 -2 |
---|---|---|---|
0 | -1 | 0 的反碼是 -1 | -1 是 0 的反碼 |
1 | -2 | 1 的反碼是 -2 | -2 是 1 的反碼 |
2 | -3 | 2 的反碼是 -3 | -3 是 2 的反碼 |
3 | -4 | 3 的反碼是 -4 | -4 是 3 的反碼 |
-0 | -1 | -0 的反碼是 -1 | -1 是 -0 的反碼 |
-1 | 0 | -1 的反碼是 0 | 0 是 -1 的反碼 |
-2 | 1 | -2 的反碼是 1 | 1 是 -2 的反碼 |
-3 | 2 | -3 的反碼是 2 | 2 是 -3 的反碼 |
能夠看出在十進制格式下,原數值與反碼的關係:
規律:
在 Go 語言中,一個數值是正數或負數,不管是何種打印方式,輸出的都會待上正負號:
fmt.Printf("1 的十進制 : %v\n",1) fmt.Printf("-1 的十進制 : %v\n",-1) fmt.Printf("-1 的二進制(簡化版): %v\n",strconv.FormatInt(-1,2)) fmt.Printf("1 的二進制 : %064b\n",1) // 佔 64 位寬,不足補 0 fmt.Printf("-1 的二進制 : %064b\n",-1) // 佔 64 位寬,不足補 0 fmt.Printf("4 的十進制 : %v\n",4) fmt.Printf("-4 的十進制 : %v\n",-4) fmt.Printf("-4 的二進制(簡化版): %v\n",strconv.FormatInt(-4,2)) fmt.Printf("4 的二進制 : %064b\n",4) // 佔 64 位寬,不足補 0 fmt.Printf("-4 的二進制 : %064b\n",-4) // 佔 64 位寬,不足補 0 // 輸出 // 1 的十進制 : 1 // -1 的十進制 : -1 // -1 的二進制(簡化版): -1 // 1 的二進制 : 0000000000000000000000000000000000000000000000000000000000000001 // -1 的二進制 : -000000000000000000000000000000000000000000000000000000000000001 // 4 的十進制 : 4 // -4 的十進制 : -4 // -4 的二進制(簡化版): -100 // 4 的二進制 : 0000000000000000000000000000000000000000000000000000000000000100 // -4 的二進制 : -000000000000000000000000000000000000000000000000000000000000100
若是咱們想要看到正確的負數的補碼形式則須要經過無符號數值類型間接實現:
^uint8(1) + 1
;fmt.Printf("int8(1) : %08b \n", int8(1)) // 佔 8 位寬,不足補 0 fmt.Printf("^int8(1) : %08b \n", ^int8(1)) // 佔 8 位寬,不足補 0 fmt.Printf("^int8(1)+1 : %08b \n", ^int8(1)+1) // 佔 8 位寬,不足補 0 fmt.Printf("uint8(1) : %08b \n", uint8(1)) // 佔 8 位寬,不足補 0 fmt.Printf("^uint8(1) : %08b \n", ^uint8(1)) // 佔 8 位寬,不足補 0 fmt.Printf("^uint8(1)+1 : %08b \n", ^uint8(1)+1)// 佔 8 位寬,不足補 0 // 輸出 // int8(1) : 00000001 // ^int8(1) : -0000010 // ^int8(1)+1 : -0000001 // uint8(1) : 00000001 // ^uint8(1) : 11111110 // ^uint8(1)+1 : 11111111
1111 1110
,接着對反碼後加一獲得原值 1 的負數形式 -1 的補碼 1111 1111
;不管是有符號數值類型仍是無符號數值類型,只要轉成補碼後進行的計算過程不須要考慮符號位的問題。
A - B = A + ( -B )
A - B = 補碼( A ) + 補碼( -B )
原子操做便是進行過程當中不能被中斷的操做。也就是說,針對某個值的原子操做在被進行的過程中,CPU 毫不會再去進行其它的針對該值的操做。不管這些其它的操做是否爲原子操做都會是這樣。爲了實現這樣的嚴謹性,原子操做僅會由一個獨立的 CPU 指令表明和完成。只有這樣纔可以在併發環境下保證原子操做的絕對安全。
Go 語言提供的原子操做都是非侵入式的。它們由標準庫代碼包 sync/atomic 中的衆多函數表明。咱們能夠經過調用這些函數對幾種簡單的類型的值進行原子操做。這些類型包括 int3二、int6四、uint3二、uint6四、uintptr 和 unsafe.Pointer 類型,共 6 個。這些函數提供的原子操做共有 5 種,即:增或減、比較並交換、載入、存儲和交換。
相關文檔如圖所示:
這裏主要須要注意的是 Uint 類型的原子操做,以 AddUint32 函數爲例
原子性的增長數值:
value := uint32(1) atomic.AddUint32(&value, 1) fmt.Printf("after call atomic.AddUint32(&value, 1) value is: %v\n", value) atomic.AddUint32(&value, 2) fmt.Printf("after call atomic.AddUint32(&value, 2) value is: %v\n", value) atomic.AddUint32(&value, 3) fmt.Printf("after call atomic.AddUint32(&value, 3) value is: %v\n", value) // 輸出 // after call atomic.AddUint32(&value, 1) value is: 2 // after call atomic.AddUint32(&value, 2) value is: 4 // after call atomic.AddUint32(&value, 3) value is: 7
原子性的減小數值:
如文檔所述,若是須要減去一個正數 c 須要經過 ^uint32(c-1)
計算獲得 c 的補碼。
const one, two, three = 1, 2, 3 value := uint32(10) atomic.AddUint32(&value, ^uint32(one - 1)) // 減一 fmt.Printf("after callatomic.AddUint32(&value, ^uint32(one - 1)) value is: %v\n", value) atomic.AddUint32(&value, ^uint32(two - 1)) // 減二 fmt.Printf("after callatomic.AddUint32(&value, ^uint32(two - 1)) value is: %v\n", value) atomic.AddUint32(&value, ^uint32(three - 1)) // 減三 fmt.Printf("after callatomic.AddUint32(&value, ^uint32(three - 1)) value is: %v\n", value) // 輸出 // after callatomic.AddUint32(&value, ^uint32(one - 1)) value is: 9 // after callatomic.AddUint32(&value, ^uint32(two - 1)) value is: 7 // after callatomic.AddUint32(&value, ^uint32(three - 1)) value is: 4
^
;