### C語言程式設計導論
### 位元運算與資料的儲存
---
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
Table of Contents
</h3>
- 二進位數字
- 資料的儲存
- 認識位元運算
- 位元運算子
- 程式範例
- 例題練習
----
<!-- .slide: data-transition="fade" -->
### 為了節省講義空間
### 從這裡開始會習慣性省略以下幾行程式
```cpp
#include<stdio.h>
int main(){
return 0;
}
```
<!-- .element: class="fragment" data-fragment-index="1" -->
---
<!-- .slide: data-transition="fade" -->
### 二進位數字
----
<!-- .slide: data-transition="fade" -->
**我們一般看到的數字都是十進位的數字**
**十進位的數字 意旨每十個數字進一位**
**因此每位數字只能是$0$~$9$**
----
<!-- .slide: data-transition="fade" -->
**那麼二進位就是每二個數字進一位**
**數字只能是$0$或者$1$**
**這特性剛好與電路通電與不通電的特性合得來**
**因此電腦的底層儲存的都是二進位的數字**
<!-- .element: class="fragment" data-fragment-index="1" -->
----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
不同進位的數字在數學上的表示法
</h3>
<p class="text-left">
<b>
基本上就是數字加小括號
右下角加小標代表進位制
</b>
</p>
- **十進位:** $(87)_{10}$
- **二進位:** $(1010111)_{2}$
- **八進位:** $(3254)_{8}$
----
<!-- .slide: data-transition="fade" -->
**換種方式來看十進位**
**我們可以把十進位數字$(1782)_{10}$粗略的這樣做拆分**
$(1782)_{10}=1\times 10^{3}+7\times 10^{2}+8\times 10^{1}+2\times 10^0$
----
<!-- .slide: data-transition="fade" -->
**想當然 二進位數字$(1011)_{2}$大概就會長這樣**
$(1\times 2^3+0\times 2^2+1\times 2^1+1\times 2^0)_{10}=(11)_{10}$
----
<!-- .slide: data-transition="fade" -->
**那我們要怎麼從十進位轉成二進位呢**
**可以使用短除法**
<!-- .element: class="fragment" data-fragment-index="1" -->
----
<!-- .slide: data-transition="fade" -->
<p class="text-left">
<b>
以十進位數字87舉例
<br>
可以發現
<br>
每次除一次2的餘數
<br>
都會由小到大
<br>
變成二進位的位數
</b>
</p>
<img src="https://hackmd.io/_uploads/Sy6-wEqqA.png" style="position: absolute; right: 150px; top: 0px;width: 30%; height: 600%">
----
<!-- .slide: data-transition="fade" -->
**那如何把二進位數字轉成十進位呢**
**不用想太多 乘完之後直接加就好**
<!-- .element: class="fragment" data-fragment-index="1" -->
----
<!-- .slide: data-transition="fade" -->
**以數字$(1011)_{2}$為例**
$(1\times 2^3+0\times 2^2+1\times 2^1+1\times 2^0)_{10}=(11)_{10}$
----
<!-- .slide: data-transition="fade" -->
<p class="text-left">
<b>
<br>
<br>
<br>
如果要用3bit表示數字的話
<br>
大概會長這樣
<br>
<br>
<br>
<br>
<br>
</b>
</p>
<img src="https://hackmd.io/_uploads/HkCkKBqqC.png" style="position: absolute; right: 120px; top: 0px;width: 30%; height: 100%">
----
<!-- .slide: data-transition="fade" -->
**補充要說的是**
**十六進位在資訊領域也是蠻常見的**
**基本上會把$10$\~$15$寫成$A$\~$F$**
**轉換方法跟前面提及的差不多**
**很trivial 所以在此不多贅述**
---
<!-- .slide: data-transition="fade" -->
### 資料的儲存
----
<!-- .slide: data-transition="fade" -->
**這邊只是粗淺的題及各資料型態是怎麼儲存**
**如果想知道完整的知識 可以去學大學的
計算機組織與架構、計算機概論與數位邏輯設計**
----
<!-- .slide: data-transition="fade" -->
**電腦是通過電壓的大小
high 或是 low
來看要存哪一種資料**
**因此只會有兩種狀態: $0$、$1$**
----
<!-- .slide: data-transition="fade" -->
**在每一個bit中 我們也只會用$0$、$1$表示**
**以下就分別來講各種C語言的資料型態如何儲存**
----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
整數int
</h3>
**用$4$bytes也就是$32$bits儲存**
**可以用$32$個格子來表示**
**第一個格子叫做sign bit
如果是$0$就是正的 $1$就是負的**

----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
整數int
</h3>
<p class="text-left">
<b>
<br>
以一個3bit的系統來說
<br>
所有能代表的數字
<br>
大概長這樣
<br>
<br>
這種表示法叫做二補數
<br>
<br>
</b>
</p>
<img src="https://hackmd.io/_uploads/SkFTBSc50.png" style="position: absolute; right: 120px; top: 0px;width: 30%; height: 100%">
----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
整數int
</h3>
<p class="text-left">
<b>
<br>
如果要把正的變成負的
<br>
要把 0 變成 1
<br>
把 1 變成 0
<br>
然後 + 1
<br>
<br>
然後你就會發現
<br>
最後的 -4 不適用
</b>
</p>
<img src="https://hackmd.io/_uploads/SkFTBSc50.png" style="position: absolute; right: 120px; top: 0px;width: 30%; height: 100%">
----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
浮點數float
</h3>
**用$4$bytes也就是$32$bits儲存**
**大致可以分為三個部分**
**sign, exponent, mantissa**

----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
浮點數float
</h3>
**sign: 正負號($S$)**
**exponent: 位移量($E$)**
**mantissa: 尾數($M$)**
**一個浮點數寫起來就會是**
$S\times M\times 2^E$
----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
浮點數float
</h3>
**舉例將$7.25$化成浮點數儲存**
1. **判斷正負: 正**
2. **將其化為二進制: $111.01$**
3. **將小數點移到最前面: $1.1101$
發現位移量為$2$ 並捨去最前面的$1$**
4. **計算$E$加上$2^8-1$其中$8$為格子數量:** $E=(2^8-1)+2=(129)_{10}=(1000 0001)_{2}$
5. **寫答案:** $01000000111010000000000000000000$
----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
浮點數float
</h3>
**可以驗算一下**
$S=+1$
$E=129-127=2$
$M=(0.1101)_{2}+1=1.8125$
$S\times M\times 2^E=7.25$
----
<!-- .slide: data-transition="fade" -->
**因為格子都是有限的**
**所以當紀錄的數字超出格子時就會溢位**
**導致數字出現錯誤**
----
<!-- .slide: data-transition="fade" -->
**以上看不懂沒關係**
**反正是大學的東西**
---
<!-- .slide: data-transition="fade" -->
### 認識位元運算
----
<!-- .slide: data-transition="fade" -->
**電腦處理位元運算是基於電路的邏輯閘**
**邏輯閘的基礎是基於布林代數**
<!-- .element: class="fragment" data-fragment-index="1" -->
**所以先來講講布林代數在幹嘛**
<!-- .element: class="fragment" data-fragment-index="2" -->
----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
布林代數
</h3>
- **布林代數是由[喬治·布爾](https://zh.wikipedia.org/zh-tw/%E4%B9%94%E6%B2%BB%C2%B7%E5%B8%83%E5%B0%94)提出**
- **主要是處理邏輯問題 將邏輯從哲學轉化成數學**
- **只有兩種元素: Ture($1$), False($0$)**
- **被信息學之父[克勞德·香農](https://zh.wikipedia.org/zh-tw/%E5%85%8B%E5%8A%B3%E5%BE%B7%C2%B7%E9%A6%99%E5%86%9C)用於處理電路問題**
- **現今主要用於數學論證與電路設計上**
----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
布林代數
</h3>
**主要有四種運算元是寫程式常用到的**
- OR $\lor$ 或
- AND $\land$ 且
- NOT $\neg$ 否
- XOR $\oplus$ 異或
----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
布林代數
</h3>
**我們可以用真值表來看所有的運算**
|$P$|$Q$|$P\lor Q$|$P\land Q$|$P\oplus Q$
|----|----|----|----|----|
|0|0|0|0|0|
|0|1|1|0|1|
|1|0|1|0|1|
|1|1|1|1|0|
<!--  -->
**左邊是$P$、$Q$分別代入的數字 右邊是運算結果**
<!-- .element: class="fragment" data-fragment-index="1" -->
----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
布林代數
</h3>
**這是NOT$\neg$的真值表**
|$P$|$\neg P$|
|----|----|
|0|1|
|1|0|
----
<!-- .slide: data-transition="fade" -->
**真值表列出的幾乎就是定義了**
**而這些也可以幫助我們簡化邏輯的表示**
<!-- .element: class="fragment" data-fragment-index="1" -->
----
<!-- .slide: data-transition="fade" -->
**設$P$、$Q$敘述為「會下雨」、「地面會溼」**
**那這樣的敘述就可以寫成$\neg P\lor Q$**
<!-- .element: class="fragment" data-fragment-index="1" -->
----
<!-- .slide: data-transition="fade" -->
**可以用真值表來驗證**
|敘述|$P$|$Q$|$\neg P\lor Q$|結果|
|----|----|----|----|----|
|沒下雨 地面沒溼|0|0|1|成立|
|沒下雨 面板溼|0|1|1|成立|
|下雨 地面沒溼|1|0|0|不成立|
|下雨 地面溼|1|1|1|成立|
----
<!-- .slide: data-transition="fade" -->
**當我們使用布林代數來處理位元問題時**
**這就稱為位元運算**
<!-- .element: class="fragment" data-fragment-index="1" -->
----
<!-- .slide: data-transition="fade" -->
**假如說我寫 $5$ or $6$ 那就會得到 $7$**

----
<!-- .slide: data-transition="fade" -->
**假如說我寫 $5$ xor $6$ 那就會得到 $3$**

---
<!-- .slide: data-transition="fade" -->
### 位元運算子
----
<!-- .slide: data-transition="fade" -->
**講了這麼多**
**回來說一下C語言之中要怎麼處理位元運算**
----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
位元運算子
</h3>
**C語言中的位元運算子只適用於整數int**
|運算|數學符號|C運算子|
|---|---|----|
|OR|$\lor$|\||
|AND|$\land$|&|
|XOR|$\oplus$|^|
|NOT|$\neg$|\~|
|左移|$\div 2$|<<|
|右移|$\times 2$|>>|
----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
位元運算子
</h3>
**比較特別的是>>、<<**
**在十進位的意思就是$\div 2$、$\times 2$**
<!-- .element: class="fragment" data-fragment-index="1" -->
**但是在二進位的意思就是左移與右移**
<!-- .element: class="fragment" data-fragment-index="2" -->
----
<!-- .slide: data-transition="fade" -->
<h3 class="text-left">
位元運算子
</h3>
$5 \times 2 = 5 << 1 = (101)_2 << 1=(1010)_2=10$
$5 \div 2 = 5 >> 1 = (101)_2 >> 1=(10)_2=2$
**左移後最右邊的位置要補$0$**
<!-- .element: class="fragment" data-fragment-index="1" -->
----
<!-- .slide: data-transition="fade" -->
**這些位元運算子也可以加個=**
**變成賦值運算子**
----
<!-- .slide: data-transition="fade" -->
**由於位元運算子在C語言優先度非常低**
**要使用時記得括號括好**
----
<!-- .slide: data-transition="fade" -->
**補充**
**有一個好用的函式__builtin_popcount()**
**可以帶入數字 計算二進位中$1$的數量**
---
<!-- .slide: data-transition="fade" -->
### 程式範例
----
<!-- .slide: data-transition="fade" -->
**二進位轉十進位
可以先用字串儲存二進位**
```cpp
// bi[]: 輸入的字串, ans: 儲存答案
void binary_to_decimal(char bi[], int &ans){
int len = strlen(bi), ret = 0, ep = 1;
// len: 字串長度, ret: 紀錄運算過程, ep: 2的次方數
for(int i = len - 1; i >= 0; i--){
ret += (bi[i] - '0') * ep;
ep <<= 1;
}
ans = ret;
}
```
----
<!-- .slide: data-transition="fade" -->
**十進位轉二進位**
**答案以字串儲存**
```cpp
void decimal_to_binary(int &n, char ans[]){
int idx = 0; // 這就是陣列編號
while(n){ // 這過程相當於短除法
ans[idx++] = (n & 1) + '0'; // (n & 1) 就是 (n % 2)
n >>= 1; // 除以 2
}
ans[idx] = '\0'; // 字串末尾一定要加上 '\0'
int len = strlen(ans); // 字串長度
for(int i = 0; i < len / 2; i++){ // 字串前後reverse過來
char tmp = ans[i]; // 做交換
ans[i] = ans[len - 1 - i];
ans[len - 1 - i] = tmp;
}
}
```
---
<!-- .slide: data-transition="fade" -->
### 例題練習
----
<!-- .slide: data-transition="fade" -->
[Zerojudge c461. apcs 邏輯運算子 (Logic Operators)](https://zerojudge.tw/ShowProblem?problemid=c461)
[Zerojudge e797. p4. 數位邏輯運算](https://zerojudge.tw/ShowProblem?problemid=e797)
[Zerojudge a132. 10931 - Parity](https://zerojudge.tw/ShowProblem?problemid=a132)
[Zerojudge b005. 布林矩陣的等價短陣](https://zerojudge.tw/ShowProblem?problemid=b005)
[Zerojudge a414. 位元運算之進位篇](https://zerojudge.tw/ShowProblem?problemid=a414)
[Zerojudge d632. C and S ??](https://zerojudge.tw/ShowProblem?problemid=d632)
[Zerojudge e253. a + b problem](https://zerojudge.tw/ShowProblem?problemid=e253)
[Zerojudge e545. 10019 - Funny Encryption Method](https://zerojudge.tw/ShowProblem?problemid=e545)
[Zerojudge e799. p6. 資工系的浪漫](https://zerojudge.tw/ShowProblem?problemid=e799)
[CSES Range Xor Queries](https://cses.fi/problemset/task/1650/)
---
<!-- .slide: data-transition="fade" -->
### 以上就是本章節的內容
{"description":"二進位數字","title":"C語言程式設計導論-位元運算與資料的儲存","contributors":"[{\"id\":\"4f67a8cd-06ae-45dc-a8e3-62c6a41e5a37\",\"add\":11138,\"del\":618}]"}