2016年9月2日 星期五

給新手的C++教學 (上冊) - 13 - 3. 進階型別的介紹

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

回到「13. 額外語法 (Extra syntax)」

上一頁

請注意,這裡只介紹小莫用過的型別,但應該已經非常非常夠用了!
為了方便介紹,小莫將其分為兩大類:「浮點數」和「整數」(註13)
  1. 浮點數
    • 特色:支援小數、有微小誤差、數字範圍大
    • 溢位會怎樣:超過最大值會變成inf,超過最小值會變成-inf
    • 除以0會怎樣:正數除以0會變成inf、負數除以0會變成-inf,0除以0會變成nan (「not a number」的縮寫)
    • 型別有哪些:
      名稱floatdoublelong double__float128
      占用記憶體32位元64位元96位元128位元
      誤差和數字範圍占用記憶體越大,誤差越小、數字範圍越大
      輸入(scanf)%f%lf不支援不支援
      輸出(printf)%f%f不支援不支援
      斜體標示 (針對__float128):如果要使用此型別,請先參考前一頁並設定好編譯參數,以使用C++11的功能
  2. 整數
    • 特色:不支援小數、沒有誤差、數字範圍小
    • 溢位會怎樣:超過最大值會變成最小值,超過最小值會變成最大值
    • 除以0會怎樣:會造成不可預知的錯誤,通常是程式出錯並停止運作
    • 型別有哪些:
      名稱boolcharshortintlong long__int128
      占用記憶體8位元8位元16位元32位元64位元128位元
      數字範圍$0\sim 1$$(-2^{占用記憶體-1})\sim (2^{占用記憶體-1}-1)$
      輸入(scanf)不支援%c不支援%d%lld不支援
      輸入(scanf)不支援%c不支援%d%lld不支援
      粗體標示 (針對char):輸入輸出皆以字元的方式進行 (而不是數字)
      斜體標示 (針對__int128):如果要使用此型別,請先參考前一頁並設定好編譯參數,以使用C++11的功能
    • 另外,整數還可以用「unsigned」修飾而有更多玩法!意者請Google搜尋「C++ unsigned 用法」
對於不支援輸入輸出的型別的其中一種處理方式 (以相加兩個「long double」為例):


#include<cstdio>
int main()
{
    double a,b;
    long double c,d,e;
    scanf("%lf%lf",&a,&b);
    c=a;
    d=b;
    e=c+d;
    double f=(double)e;//請注意,「f」的誤差是「double」等級 (而不是「long double」等級)
    printf("%f\n",f);
    return 0;
}

這種方式並不是最好的 (支援「long double」等級的加法運算,但不支援「long double」等級的輸入輸出)
如果要讓程式支援「long double」等級的輸入輸出,請自行處理每個位數的輸入輸出,但這會複雜許多 (真的複雜許多,別怪我沒警告你),方法如下 (不想看的話可以直接跳過):

#include<cstdio>
bool IsDigit(char c)
{
    return '0'<=c&&c<='9';
}
int main()
{
    long double a=0;
    {
        char c;
        while(1)
        {
            scanf("%c",&c);
            if(!IsDigit(c))break;
            a=a*10+(c-'0');
        }
        if(c=='.')//如果有小數點 
        {
            long double digit=0.1;
            while(c)
            {
                scanf("%c",&c);
                if(!IsDigit(c))break;
                a=a+digit*(c-'0');
                digit=digit/10;
            }
        }
    }
    {
        int count=0;
        while(a/10>=1)
        {
            a=a/10;
            count=count+1;
        }
        while(count>=-50)//假設要輸出小數點以下50位數
        {
            if(0<=a&&a<1)printf("0");
            else if(1<=a&&a<2)
            {
                printf("1");
                a=a-1;
            }
            else if(2<=a&&a<3)
            {
                printf("2");
                a=a-2;
            }
            else if(3<=a&&a<4)
            {
                printf("3");
                a=a-3;
            }
            else if(4<=a&&a<5)
            {
                printf("4");
                a=a-4;
            }
            else if(5<=a&&a<6)
            {
                printf("5");
                a=a-5;
            }
            else if(6<=a&&a<7)
            {
                printf("6");
                a=a-6;
            }
            else if(7<=a&&a<8)
            {
                printf("7");
                a=a-7;
            }
            else if(8<=a&&a<9)
            {
                printf("8");
                a=a-8;
            }
            else if(9<=a&&a<10)
            {
                printf("9");
                a=a-9;
            }
            else
            {
                printf("錯誤!!!不該發生這種情況的!!!\n");
                return 0;
            }
            if(count==0)printf(".");//剛剛輸出的是個位數字,是時候接小數點了 
            count=count-1;
            a=a*10;
        }
        printf("\n");
    } 
    return 0;
}

這樣真的可以支援輸入輸出「long double」等級的位數嗎?
把程式碼中兩個「long double」都改成「double」,輸入「123456789.0123456789」看看
你會發現「double」和「long double」之間的差異
「double」版本
「long double」版本
為甚麼變成Windows 10畫面了呢?因為之前用的是高雄中學電腦教室的電腦啦XDD (現在用的是家裡的電腦)


如果需要儲存更多位數怎麼辦?
抱歉,目前的方法只有自行模擬直式運算 (其中一種方法是用很大的陣列儲存一個一個的位數,這種方式比上面那個恐怖的東西複雜10倍以上)
或者,請祈禱更新版的C++ (C++17快出來了) 會提供大數型別

下一頁

感謝:
(版權所有 All copyright reserved)

沒有留言:

張貼留言

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

注意:只有此網誌的成員可以留言。