為什麼int8的範圍是[-128,127]

Remember發表於2020-10-25

今天這篇文章非常基礎。

前幾天看到的 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 協議》,轉載必須註明作者和本文連結
吳親庫裡

相關文章