2016年4月29日 星期五

給新手的C++教學 (上冊) - 3. 認識運算子 (Acquaintance to operators)

回到「給新手的C++教學 (上冊)」

上一章

利用先前學到的概念,我們已經有能力可以讓程式跟人互動了!
不過,這樣似乎還缺了甚麼?
我們的程式目前好像只能模仿我們的輸入

如果我們的程式除了記憶力之外,還可以擁有強大的運算能力,把計算結果輸出給我們,那該有多好啊~
這樣,就可以真正的利用程式來幫助我們計算了!

要讓我們的程式開竅,我們可以使用「運算子 (Operator)」

運算子是甚麼呢?
千萬別被它的名稱嚇到了,其實「運算子」就只是「加減乘除」而已XD
比較需要注意的是,「乘法」的符號是「*」,「除法」的符號是「/」
(WTF原來這麼簡單?!)

還記的你在寫數學題目時會用到的算式嗎?
舉個例子:1+2的答案是?
你一定知道答案是3
信不信,電腦在學會「C++」之後 (電腦學會C++?請見第一章),也知道答案是3!
不信嗎?我們來做個小測試:

#include<cstdio>
int main()
{
    int a=1,b=2;
    printf("%d\n",a+b);
    return 0;
}

不用說你也知道要做甚麼吧?
趕快在Dev-C++裡面把這份程式碼打出來,然後按下鍵盤上的「F11鍵」查看程式執行結果吧!

程式輸出3了!
(可能有細心的讀者發現,我把Dev-C++的背景改成黑色了,這只是個人習慣,因為我覺得黑色背景比較好看XD)

在打出這份程式碼的過程中,聰明的你可能會注意到
原本的寫法
int a,b;
變成了
int a=1,b=2;
意思就是,我們定義型別為int的兩個變數,a和b (不懂?複習一下第二章吧)
然後把a設定成1,b設定成2
這種「將剛被定義出來的變數設定數值」的動作
我們稱之為「初始化 (Initialization)」(這個看起來很難的字眼,其實真的就是這麼簡單XD)
如果你有使用過Windows XP作業系統,你可能在登入畫面看過這個字眼
沒錯,這時候「Windows XP」這個巨大的程式,正在設定它裡面每個變數的數值

這是Windows XP的登入畫面,有時候會顯示「正在初始化...」
回到正題
程式輸出3了!電腦果然厲害!
那麼該來考考電腦了
現在,請你問問看電腦
2+3
567+789
273*7122
91/13
87411-24005
105-324
分別是多少呢?

答案是
2+3=5
567+789=1356
273*7122=1944306
91/13=7
87411-24005=63406
105-324=-219
你的電腦答對了嗎?
現在,請你嘗試問電腦更難、更刁鑽的問題
甚麼情況下電腦會答錯呢?

電腦真的有可能會算錯喔
舉兩個例子:
100000*100000
5/3

如果你覺得驚訝,代表你是個正常人

100000*100000的答案是「10000000000」
但是電腦卻回答了「1410065408」
5/3的答案是「1.6666666666......」
但是電腦卻回答了「1」
Why?
我們來回顧一下程式碼:

#include<cstdio>
int main()
{
    int a=100000,b=100000;
    printf("%d\n",a*b);
    return 0;
}
 

以及

#include<cstdio>
int main()
{
    int a=5,b=3;
    printf("%d\n",a/b);
    return 0;
}
 

可以發現,我們的程式是用兩個int去進行計算的
數學上,整數的加減乘法具有封閉性
也就是兩個整數相加、相減、相乘後的結果還是一個整數
程式上也一樣,「int」的運算也具有封閉性,不過封閉性更強了,連「除法」也具有封閉性!
事實上,你對兩個int進行任何運算,結果一定還是一個int!

int無法處理小數,所以「1.6666666666......」就捨去小數部分,變成了「1」,合情合理
(如果結果是「-1.6666666666......」,以int的型別儲存後就會變成「-1」) (註10)

至於「1410065408」,這就揭露了int的另外一個秘密了

事實上,你電腦的記憶體再怎麼多,還是有限的,因此不可能儲存很大很大的數字
對於int這種類型的變數,電腦會撥出大小為「32個位元」的記憶體去儲存它 (註1)
甚麼是位元?
就是0或1,電腦中最基本的儲存和運算單位

也就是說,int其實就是一個「最多32位數的二進位數字」
哪有?我們看到的int怎麼看都是一個十進位數字啊
那是因為電腦學會了C++,所以知道要怎麼把這個二進位數字轉換成十進位數字,輸出給我們看
那「負數」怎麼辦呢?
電腦學會了C++,很聰明的解決了這個問題
方法就是把32個位元的其中一個位元拿掉,這個位元就不用來表示二進位數字的其中一個位數了
電腦拿這個位元來表示這個數字的正負號
如果這個位元是0,表示這個數字是正數
如果這個位元是1,表示這個數字是負數
很聰明吧!

所以,剩下的「31個位元」所能表示的數字範圍就是「$0\sim 2^{31}-1$」(表示正數時) 或「$-2^{31}\sim -1$」(表示負數時) 了 (每個位元有2種情況 (0或1),因此31個位元可以有$2^{31}$種組合,這牽涉到一點數學排列組合的知識)
也就是「$0\sim 2147483647$」或「$-2147483648\sim -1$」(註3)
因此,int的最小值是「-2147483648」,最大值是「2147483647」

也因此,如果int的計算結果超出了「-2147483648~2147483647」的範圍,電腦就會因為撥出來給int變數的記憶體不夠而算錯喔~
這種情況,我們稱作「溢位 (Overflow)」,這是我們寫程式的人需要注意的,因為它會導致錯誤的計算結果

上面我們有提到,int無法處理小數,因此5/3的答案是「1」
不知聰明的你有沒有想到
那我們乾脆就用可以處理小數的「float」來計算就好啦!

廢話不多說,馬上來試試看!

#include<cstdio>
int main()
{
    float a=5,b=3;
    printf("%f\n",a/b);
    return 0;
}
 

要注意的是,float也滿足加減乘除的封閉性,因此計算出來的東西也是一個float,要用「%f」來輸出,而不是int的「%d」

用「float」計算5/3的結果
耶!果然有小數了!
等等,為甚麼最後一位是7?
剛剛有提到,電腦的記憶體是有限的,因此不可能儲存很大很大的數字,當然也不可能儲存很多很多的小數
float是用「科學記號」的方式儲存的,換句話說,「24005」會被當作「$2.4005*10^{4}$」來看待 (事實上不是如此,請見「註2」)
其中,「2.4005」和「4」是分開儲存的,和int一樣會被轉成二進位數字,儲存在電腦撥出來給它的記憶體中 (事實上不是如此,請見「註2」)
這兩個數字當然也有其範圍極限

還記得第二章提到的誤差問題嗎?
就是這樣來的喔~

綜合起來
float的範圍約落在「$-10^{38}\sim -10^{-38}$」、「0」、「$10^{-38}\sim 10^{38}$」
誤差約「0.00001% (也就是$10^{-7}$)」
這裡看不懂沒關係,你只要知道float有誤差就好 ^_^ (float非主流,見註6)

最後,再跟大家報告一個好消息
數學上,我們習慣「先乘除,後加減」
因此「$5\times 6+9\div 3$」的答案是「$5\times 6+9\div 3=(5\times 6)+(9\div 3)=30+3=33$」
而不是「$5\times 6+9\div 3=30+9\div 3=39\div 3=13$」喔
程式上,「先乘除,後加減」的規則也是成立的!
而且,還可以使用括號喔!

以下提供兩個例子,讓你深入了解C++程式的奧妙!
「先乘除,後加減」

#include<cstdio>
int main()
{
    int a=5,b=6,c=9,d=3;
    printf("%d\n",a*b+c/d);
    return 0;
}
 
「先乘除,後加減」,因此程式輸出「33」

「有括號,要先算」

#include<cstdio>
int main()
{
    int a=5,b=6,c=9,d=3;
    printf("%d\n",a*(b+c)/d);
    return 0;
}
 
「有括號,要先算」,因此程式輸出「25」

現在,換你了!
你玩出了甚麼東西呢?

下一章

感謝:pr3pony、李旺陽學長、WillyPillow
(版權所有 All copyright reserved)

8 則留言:

  1. 所以int不是取高斯函數喔==

    回覆刪除
    回覆
    1. 不是哦,是直接省略小數部分
      您可以用以下程式碼來驗證這件事:

      #include
      int main()
      {
      float a=-1.6;
      int b=a;
      printf("%d\n",b);
      return 0;
      }

      輸出會是「-1」而不是「-2」哦
      感謝您的發問~^_^

      刪除
    2. Oops...Sorry程式碼爛掉了
      補上

      #include<cstdio>
      int main()
      {
          float a=-1.6;
          int b=a;
          printf("%d\n",b);
          return 0;
      }

      刪除
  2. 不好意思ㄝ我從這裡看不懂 所以,剩下的「31個位元」所能表示的數字範圍就是「0∼231−1」了
    也就是「0~2147483647」
    因此
    int的最小值是「-2147483648」(「負數的0」和「正數的0」是相同的,因此為了避免浪費,電腦會把「負數的0」當作「-2147483648」) (註3)
    int的最大值是「2147483647」

    因此,如果int的計算結果超出了「-2147483648~2147483647」的範圍,電腦就會因為撥出來給int變數的記憶體不夠而算錯喔~

    回覆刪除
    回覆
    1. 哈囉,感謝您的提問~
      每個位元有2種情況 (0或1),因此31個位元可以有2^31種組合,可以用來表示 0~2^31-1 (正數) 或 -1~-2^31 (負數)
      這段可能需要一些排列組合的知識才會比較好懂哦~

      刪除
    2. 已更新本文章,您可以再看一次試試看能不能看懂哦~

      刪除
  3. 想請問一下
    我要輸入兩個數字並輸出兩數的乘積
    這樣為甚麼錯呢??

    #include <iostream>
    using namespace std;
    int main(int argc, char **argv)
    {
        double num1, num2, product;
        product = num1 * num2;
        
        cout << "Please enter two floating-point numbers: ";
        cin >> num1 >> num2;
        cout << "\nThe product is " << product;    
    }

    回覆刪除
    回覆
    1. 請先設定好num1、num2的數值才能用它們算出正確的結果哦~
      以下才是正確的寫法:

      #include <iostream>
      using namespace std;
      int main(int argc, char **argv)
      {
          double num1, num2, product;
          
          cout << "Please enter two floating-point numbers: ";
          cin >> num1 >> num2;
          product = num1 * num2;
          cout << "\nThe product is " << product;    
      }

      刪除

歡迎留言或問問題~
若您的留言中包含程式碼,請參考這篇
如果留言不見了請別慌,那是因為被google誤判成垃圾留言,小莫會盡快將其手動還原