National Central University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try an AI Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

3

Local AI

--

Uedu Code

--

中央大學 AQI 80 24°C PM2.5 21
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 04

整數的表示:二補數與溢位

有號整數怎麼存、加法為什麼會溢位
Interaction 25 minute(s)

Learning Objectives

  • 說出 n 位元無號整數與二補數有號整數能表示的範圍
  • 用「最高位的權重是負的」讀出二補數的值,用「每一位反轉再加 1」求一個數的負數
  • 說明同一組位元在無號與有號解讀下代表不同的值
  • 判斷一個加法有沒有溢位:無號看最高位有沒有進位出去,有號看兩正得負、兩負得正

觀看次數為什麼會有上限

2014 年,PSY 的《江南 Style》在 YouTube 的觀看次數逼近 2,147,483,647。YouTube 公開表示:原本以為不會有影片的觀看次數超過「32 位元整數」的上限,所以已經把計數器改成 64 位元。

2,147,483,647 這個數字看起來很隨機,其實是 2³¹ - 1。為什麼是它?為什麼不是 2³² - 1?上一課學到 8 個位元可以表示 0~255,那 -5 這種負數要怎麼存?

這一課要回答:

  1. 電腦怎麼用位元表示負數?
  2. 為什麼 127 再加 1,在 8 位元的世界裡會變成 -128?
  3. 怎麼判斷一個加法的結果「已經錯了」?

無號整數

只表示 0 與正數的整數叫無號整數(unsigned integer)。n 個位元有 2ⁿ 種組合,從 0 開始用,範圍是 0 到 2ⁿ - 1:

位元數 無號範圍
8 0~255
16 0~65,535
32 0~4,294,967,295

負數怎麼存:二補數

直覺的做法是拿最高位當正負號(0 是正、1 是負),其餘 7 位表示大小,這叫符號與大小(sign-magnitude)表示法。它有兩個麻煩:

  • 0 有兩種寫法:0000 0000 是 +0,1000 0000 是 -0
  • 加法要先看正負號再決定做加法還是減法,電路比較複雜

現代電腦幾乎都用二補數(two's complement)。規則只有一條:最高位的權重是負的。8 位元時,各位的權重是:

位元     b7    b6   b5   b4   b3   b2   b1   b0
權重   -128    64   32   16    8    4    2    1

例如 1111 1011 = -128 + 64 + 32 + 16 + 8 + 2 + 1 = -5。

位元 無號解讀 二補數解讀
0111 1111 127 127(最大的正數)
0000 0001 1 1
0000 0000 0 0
1111 1111 255 -1
1111 1110 254 -2
1000 0001 129 -127
1000 0000 128 -128(最小的負數)

觀察這張表:

  • 最高位是 1 就是負數,所以最高位也叫符號位元(sign bit)
  • 正數的寫法和無號整數完全一樣
  • 0 只有一種寫法
  • 範圍是 -128 到 127:負數比正數多一個,因為 0 佔掉了「正的那一半」的一個位置。n 位元的範圍是 -2ⁿ⁻¹ 到 2ⁿ⁻¹ - 1

位元本身不帶「有沒有正負號」的資訊。同一組 1111 1111,當成無號是 255,當成二補數是 -1;怎麼解讀,是程式(變數的型別)決定的。

同一組位元,兩種讀法

兩種解讀用同一套方法:是 1 的位就加上它的權重。唯一的差別在第一格:無號的權重是 128,二補數是 -128。看兩個總和在第一格就分道揚鑣,之後每一格加的都一樣。

取負數:每一位反轉,再加 1

要求一個數的負數,二補數有個固定的步驟:每一位反轉(0 變 1、1 變 0),再加 1。

  4   = 0000 0100
反轉  = 1111 1011
加 1  = 1111 1100   = -128 + 64 + 32 + 16 + 8 + 4 = -4

為什麼可行?一個數和它的反轉相加,每一位都是 1:0000 0100 + 1111 1011 = 1111 1111,也就是 -1。所以「反轉」等於「-x - 1」,再加 1 就是 -x。

反過來也一樣:-4 反轉再加 1,就回到 4。

取負數的過程:4 變成 -4

先看「反轉」那一列一格一格長出來;接著從最右邊加 1。最右邊兩位都是 1,1 + 1 = 2,留下 0、往左進位;進位一路傳到遇見 0 的那一位才停。

為什麼電腦選二補數

二補數最大的好處是:加法電路不必管正負號。

  • 正數加負數、負數加負數,都直接用同一個「逐位相加」的電路,算出來就是對的(只要不超出範圍)
  • 減法 a - b 可以變成 a + (-b),而 -b 就是 b 反轉加 1;所以 CPU 只需要加法器和反轉電路,就能做加減法
  • 0 只有一種寫法,比較兩個數是否相等也簡單

第 09 課會用邏輯閘做出真正的加法器,那時就會看到這套電路怎麼同時處理正數與負數。

互動練習:二補數與溢位

上方切換「無號」與「有號二補數」;A、B 可以點位元格或輸入十進位。按「A + B」會列出每一位的進位,並標出無號溢位與有號溢位;按「取負」會列出反轉再加 1 的過程。依任務操作:先算 127 + 1,再只用位元格讓 A 變成 -1、把 5 取負、找兩個負數相加變成正數的例子,最後切到無號模式算 255 + 1。

溢位:結果超出能表示的範圍

位元數固定,能表示的範圍就固定。計算結果超出範圍,存下來的值就不對了,這叫溢位(overflow)。很像汽車的里程表:六位數的里程表跑到 999999 再多一公里,就變回 000000。

無號溢位:最高位相加後還有進位,這個進位沒有地方放,就被丟掉了。

  1111 1111   (255)
+ 0000 0001   (1)
───────────
1 0000 0000   第 9 位的 1 被丟掉,只剩 0000 0000 = 0

有號溢位:結果超出 -128~127。判斷方法很簡單:

情況 會不會溢位
正數 + 負數 不會(結果一定介於兩者之間)
正數 + 正數 結果變成負數,就是溢位。例如 127 + 1 = -128
負數 + 負數 結果變成正數(或 0),就是溢位。例如 -100 + -50 = 106

CPU 做完加法會同時設定兩個旗標:進位旗標(無號溢位)與溢位旗標(有號溢位)。CPU 不知道你把位元當成有號還是無號,所以兩個都算好,由程式決定要看哪一個。

模擬 8 位元的加法

Python 的整數會自動變大,不會溢位;這裡用 % 256 只留下最低 8 個位元,模擬 8 位元的電腦。raw 是那 8 個位元當成無號的值,大於等於 128 表示最高位是 1,就減 256 變成負數。看四個加法裡哪幾個的答案錯了。

常見的整數大小與真實世界的溢位

程式語言裡的整數型別通常有固定的位元數:

位元數 二補數有號範圍 無號範圍
8 -128~127 0~255
16 -32,768~32,767 0~65,535
32 -2,147,483,648~2,147,483,647 0~4,294,967,295
64 約 ±9.22 × 10¹⁸ 0~約 1.84 × 10¹⁹

開頭的 YouTube 計數器用的是 32 位元有號整數,上限正是 2³¹ - 1 = 2,147,483,647。

溢位不只是數字顯示錯誤。1996 年,歐洲太空總署的亞利安 5 號(Ariane 5)火箭首次發射,升空後不到一分鐘就偏離航道並自毀。調查報告指出,導航軟體把一個 64 位元浮點數轉成 16 位元有號整數時,數值太大放不下(16 位元最大只到 32,767),引發了一連串錯誤。這段程式沿用自前一代火箭,在舊火箭上這個數值從來不會那麼大。

所以寫程式時要先想:這個數最大可能到多少?選的型別裝得下嗎?

  • Python 的整數會依需要自動加大,不會溢位(代價是比固定大小的整數慢一些)
  • C、C++、Java、資料庫欄位的整數是固定大小,超出範圍時有的會悄悄繞回、有的會報錯,依語言而定

想一想:二補數

1.

8 位元二補數 1111 1110 代表多少?

2.

8 位元二補數能表示的範圍是?

3.

-6 的 8 位元二補數是哪一個?

第 1 次作答

想一想:溢位

1.

下面的程式模擬 8 位元有號整數的加法,會印出什麼?

raw = (100 + 50) % 256
if raw >= 128:
    raw = raw - 256
print(raw)
2.

8 位元有號整數相加,哪一種情況一定不會溢位?

3.

位元 1111 1111 當成無號整數與二補數有號整數,分別是多少?

第 1 次作答

重點整理

  • n 位元無號整數的範圍是 0~2ⁿ - 1;8 位元是 0~255。
  • 二補數:最高位的權重是負的(8 位元時是 -128);最高位是 1 就是負數,0 只有一種寫法。n 位元範圍是 -2ⁿ⁻¹~2ⁿ⁻¹ - 1,8 位元是 -128~127。
  • 取負數=每一位反轉,再加 1。二補數讓加減法共用同一套加法電路。
  • 位元本身不帶正負號資訊;同一組 1111 1111,無號是 255、二補數是 -1。
  • 溢位是結果超出範圍:無號看最高位有沒有進位出去;有號看「兩正得負、兩負得正」,正數加負數不會溢位。
  • 32 位元有號整數的上限是 2,147,483,647;選型別前要先想資料最大可能到多少。

下一課:整數處理完了,那 3.14、0.1 這種有小數的數呢?為什麼在很多程式語言裡 0.1 + 0.2 不等於 0.3?第 05 課看浮點數。

這一課完成了!
Next lesson:浮點數:為什麼 0.1 + 0.2 不等於 0.3
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。