1 機器數和真值
1.1 機器數
一個數在計算機中的二進制表示形式, 叫做這個數的機器數。機器數是帶符号的,在計算機用一個數的最高位存放符号, 正數為0, 負數為1.比如,十進制中的數 +3 ,計算機字長為8位,轉換成二進制就是00000011。如果是-3 ,就是 10000011 。那麼,這裡的 00000011 和10000011 就是機器數。計算機中的數字是以二進制補碼的方式存儲的。
1.2 真值
因為第一位是符号位,是以機器數的形式值就不等于真正的數值。例如上面的有符号數 10000011,其最高位1代表負,其真正數值是 -3 而不是形式值131(10000011轉換成十進制等于131)。是以,為差別起見,将帶符号位的機器數對應的真正數值稱為機器數的真值。
例1:0000 0001的真值 = +000 0001 = +1,10000001的真值 = –000 0001 = –1
例2:在C語言中有以下程式段:
連結:https://www.nowcoder.com/questionTerminal/ef47c08580b24f8582b629459b224ea0
來源:牛客網
char ch=-1;
printf(“%02x,%02x”,ch,(unsigned char)ch);
則輸出的結果是:ffffffff,ff
(1) %x表示十六進制整數輸出,是以char要擴充成四位元組的int。%02x表示輸出 最少 2位,不足補0.
(2) 負數在計算機中是以補碼形式存在的,正整數的補碼是本身,負數的補碼是除符号位以外各位都取反加一,也就是如果一個32位的負數原來是1(符号位)00 ... ... 01, 取反後是1(符号位)11 ... ... 10,然後再加1,就是1(符号位)11 ... ... 11,就是32個1。是以第一個是ffff。第一個ch=-1,負數拓展到32位需要補1,是以是ffffffff.
(3) 要擴充的資料類型為無符号的 (unsigned char), 用0來填充長資料類型的高位元組,此時-1在記憶體的二進制存儲(11111111 )擴充為int即00000000 00000000 00000000 11111111(ff)。第二個ch先轉換為無符号數ff(255), 正數拓展補0,由于最少輸出2位,是以是ff
2 原碼
原碼就是符号位加上真值的絕對值, 即用第一位表示符号, 其餘位表示值. 比如如果是8位二進制:
[+1]原 =0000 0001
[-1]原 =1000 0001
第一位是符号位. 因為第一位是符号位, 是以8位二進制數的取值範圍就是:
[1111 1111 ,0111 1111],即:
[-127 , 127]
原碼是人腦最容易了解和計算的表示方式.
3 反碼
反碼的表示方法是:正數的反碼是其本身;負數的反碼是在其原碼的基礎上, 符号位不變,其餘各個位取反。
[+1] =[00000001]原 = [00000001]反
[-1] =[10000001]原 = [11111110]反
可見如果一個反碼表示的是負數, 人腦無法直覺的看出來它的數值. 通常要将其轉換成原碼再計算。
4 補碼
補碼的表示方法是:正數的補碼就是其本身;負數的補碼是在其原碼的基礎上, 符号位不變, 其餘各位取反, 最後+1. (即在反碼的基礎上+1)
[+1] =[00000001]原 = [00000001]反 = [00000001]補
[-1] =[10000001]原 = [11111110]反 = [11111111]補
對于負數, 補碼表示方式也是人腦無法直覺看出其數值的. 通常也需要轉換成原碼在計算其數值.
5 為何要使用原碼, 反碼和補碼
現在我們知道了計算機可以有三種編碼方式表示一個數. 對于正數因為三種編碼方式的結果都相同:
[+1] =[00000001]原 = [00000001]反 = [00000001]補
是以不需要過多解釋. 但是對于負數:
[-1] =[10000001]原 = [11111110]反 = [11111111]補
可見原碼, 反碼和補碼是完全不同的. 既然原碼才是被人腦直接識别并用于計算表示方式, 為何還會有反碼和補碼呢?
首先, 因為人腦可以知道第一位是符号位, 在計算的時候我們會根據符号位, 選擇對真值區域的加減. 但是對于計算機, 加減乘數已經是最基礎的運算, 要設計的盡量簡單. 計算機辨識”符号位”顯然會讓計算機的基礎電路設計變得十分複雜! 于是人們想出了将符号位也參與運算的方法. 我們知道, 根據運算法則減去一個正數等于加上一個負數, 即: 1-1 = 1 + (-1) = 0 , 是以機器可以隻有加法而沒有減法, 這樣計算機運算的設計就更簡單了.
于是人們開始探索 将符号位參與運算, 并且隻保留加法的方法.
(1)首先來看原碼。計算十進制的表達式: 1-1=0
1 - 1 = 1 +(-1) = [00000001]原 + [10000001]原 = [10000010]原 =-2
如果用原碼表示, 讓符号位也參與計算, 顯然對于減法來說, 結果是不正确的.這也就是為何計算機内部不使用原碼表示一個數.
(2)為了解決原碼做減法的問題, 出現了反碼。計算十進制的表達式:
1-1=0
1 - 1 = 1 +(-1)
= [0000 0001]原 + [1000 0001]原
= [0000 0001]反 + [1111 1110]反
= [1111 1111]反 = [1000 0000]原
= -0
發現用反碼計算減法, 結果的真值部分是正确的. 而唯一的問題其實就出現在”0”這個特殊的數值上. 雖然人們了解上+0和-0是一樣的, 但是0帶符号是沒有任何意義的. 而且會有[0000 0000]原和[10000000]原兩個編碼表示0.
(3)補碼的出現, 解決了0的符号以及兩個編碼的問題:
1-1 = 1 +(-1)
= [0000 0001]原 + [1000 0001]原
= [0000 0001]補 + [1111 1111]補
= [0000 0000]補=[0000 0000]原
這樣0用[0000 0000]表示, 而以前出現問題的-0則不存在了.而且可以用[1000 0000]表示-128:
使用補碼, 不僅僅修複了0的符号以及存在兩個編碼的問題, 而且還能夠多表示一個最低數. 這就是為什麼8位二進制, 使用原碼或反碼表示的範圍為[-127, +127], 而使用補碼表示的範圍為[-128, 127].
因為機器使用補碼, 是以對于程式設計中常用到的32位int類型, 可以表示範圍是: [-2^31, 2^31-1] 因為第一位表示的是符号位.而使用補碼表示時又可以多儲存一個最小值.
6 小數的二進制與十進制轉換
6.1 十進制轉二進制
整數和小數分别轉換:
(1)整數除以2,商繼續除以2,得到0為止,将餘數逆序排列。
(2)小數乘以2,取整,小數部分繼續乘以2,取整,得到小數部分是0為止,将整數順序排列。
例如:22.8125轉二進制
整數除以2,商繼續除以2,得到0為止,将餘數逆序排列。
22 / 2 11 餘0
11/2 5 餘 1
5 /2 2 餘 1
2 /2 1 餘 0
1 /2 0 餘 1
是以22的二進制是10110
小數乘以2,取整,小數部分繼續乘以2,取整,得到小數部分0為止,将整數順序排列。
0.8125x2=1.625 取整1,小數部分是0.625
0.625x2=1.25 取整1,小數部分是0.25
0.25x2=0.5 取整0,小數部分是0.5
0.5x2=1.0 取整1,小數部分是0,結束
是以0.8125的二進制是0.1101
十進制22.8125等于二進制10110.1101
1.2 二進制轉十進制
使用按權展開求和法,小數點左邊是2的正數次方,從0開始;小數點右邊是2的負數次方,從-1開始。
例如将101.111(2)轉換成十進制數
1*(2^2)+0*(2^1)+1*(2^0) # 整數部分
+1*(2^(-1))+1*(2^(-2))+1*(2^(-3)) # 小數部分
=5.875