今天這篇文章非常基礎。
前幾天看到的 go
一道題目,其實和 go
本身並沒有多大關係。
func main() {
問b的值是多少?如果直接說 128,那可能還需要再去補補,畢竟 int8 的範圍在 『-128,127』。
這道題的正確答案是 -128。那麼問題來了:
為什麼 int8 的範圍是 『-128,127』?
答案為什麼是 -128?
在開始之前,我們先來個簡單介紹。
一個數在計算機中的二進位制表示方式,叫做這個數的機器數。機器數是帶符號的,計算機用最高數位存放符號,正數為0,負數為1。
打個比方
var number int8 = 3
我們定義一個 number 的變數,它的型別是 int8,轉換成二進位制就是 00000011,如果是 -3,那麼二進位制就是 10000011。這裡的00000011 和 10000011 就是機器數。
因為機器數的第一位是符號位,所以機器數的形式值就不等於真正的數值,比如上面的 10000011 最高位 1 表示負,真正的值是 -3 ,而不是 131(10000011 二進位制轉為十進位制等於131)。所以,為了區分,就把帶符號位的機器數真正對應的數值稱為機器數的真值。
0000 0001 的真值是 +000 0001 = +1
我們接著去了解原碼,反碼以及補碼。
原碼
原碼就是符號位加上真值的絕對值。比如下面這個
[+1]原 = 0000 0001
原碼是最容易理解的。因為第一位是符號位,所以 8 位的二進位制原碼的取值範圍是
[11111 1111,0111 1111] 即 [-127,127]
反碼
正數的反碼是它本身,負數的反碼是在其原碼的基礎上,符號位不變,其餘各個位取反
[+1] = [0000 0001]原 = [0000 0001]反
這樣的話,如果一個反碼錶示的是負數,你無法直觀的看出它的數值,通常需要轉化成原碼再進行計算。
補碼
正數的補碼就是它本身。負數的補碼是在其原碼的基礎上,符號位不變,其餘位取反,最後 +1。也就是在反碼的基礎上 +1。
同理,補碼錶示形式也是人腦無法直觀看出數值的,也需要轉化成原碼再計算其數值。
從上面可以看出,原碼,反碼和補碼是完全不同的,只有原碼才是被人腦直接識別並用於計算表達方式的。為什麼還需要反碼和補碼?
對於計算機來說,加減乘除已經是基礎的運算了,要設計的儘量簡單,計算機識別 “符號位”顯然會讓計算機的基礎電路設計變得複雜。於是想到把符號位也參與到運算中。我們知道,根據運演算法則,減去一個數等於加上一個負數。1-1 = 1+(-1) = 0 因此計算機可以只有加法沒有減法。
我們先看原碼,十進位制的表示式:1-1=0
如果用原碼錶示,讓符號位也參與運算,顯然對於減法來說,結果不是正確的。這也就是為何計算機內部不使用原碼錶示一個數。
接著,為了解決原碼做減法的問題,出現了反碼:
可以發現,如果使用反碼計算減法,結果的真值的部分是正確的,但是引發了新的問題,雖然在理解上 +0 和 -0 是一樣的,但是 0 帶符號是沒有任何意義的。而且會有 [0000 0000] 和 [1000 0000] 兩個編碼表示0。
補碼終於要閃亮登場了。
這樣,0用[0000 0000] 表示,之前的 -0 問題就不存在了,而且可以用[1000,0000] 表示-128:
在用補碼運算的結果中,[1000 0000]補 的值就是 -128。實際上是使用之前的 -0的補碼來表示 -128,所以 -128 並沒有原碼和反碼的表示。這也是為什麼 int8 使用原碼或者反碼錶示的範圍為[-127,127]。使用補碼,不僅僅修復了 0 的符號以及存在兩個編碼的問題,而且還能多表示一個最低數。
好了,我們再回到問題的本身。因為 var b int8 = -128 / a 不是常量表示式,因此 untyped 常量 -128 隱式轉換為 int 8型別(和a一樣),所以 -128 / a 的結果是 int8 型別,值是 128。超出了 int8 的範圍,因為結果不是常量,允許溢位,128的二進位制表示式是[1000 0000],正好是 -128 的補碼,因此答案是 -128。
參考文章
本作品採用《CC 協議》,轉載必須註明作者和本文連結