iT邦幫忙

2

【魔鬼般的c++語法細節】(3) 為什麼long long L = 10000000*10000000;會產生溢位呢?

c++

為了避免讀者不了解「溢位」是什麼,
小馬從頭講起,
在c++中,一個變數型態的位元數量是有限的,
所以能夠表示的數字數量自然也是有限的

普通常見的int為32個位元

譬如最常見的int好了,總共有32個位元,
每個位元有01兩種可能,
所以能夠表示的整數數量有2的32次方這麼多種可能,
int希望可以表示負數和正數,
int可表示的範圍為: -2^31~2^31-1
確切用數字來寫的話,
int所能表示的最小數字-2147483648
int所能表示的最大數字2147483647

那麼…如果把這個「最大數字」再加一會怎麼樣?
實際來測試看看程式:

#include <iostream>

int main()
{
    int max_interger = 2147483647;
    std::cout << max_interger+1 << std::endl;
    return 0;
}

結果為-2147483648
哇鳴~ 數字彷彿會循環一樣,
「最大數字」+1之後就變成「最小數字」了,
因為「最大數字」+1超過int所能表示的數字範圍了,
這種現象稱為「溢位

長整數型別介紹- long long

那在c++要如何表示更大的整數呢?
在c++中有長整數型別long long (注意: 是long long不是long,在不同機器上測試,long型別可能是32位元或64位元),
有64個位元之多,
long long可表示的範圍為: -2^63~2^63-1
#include <climits>內有定義LLONG_MAX這個字,
表示long long可表示的「最大數字」

程式範例

#include <iostream>
#include <climits>

int main()
{
    std::cout << LLONG_MAX << std::endl;
    return 0;
}

結果為9223372036854775807
可以看到long long可表示的「最大整數」比int大很多,
當我們覺得int不夠用時,可以考慮使用long long

用long long 測試2147483647+1吧

將第一次的測試程式中的int max_interger = 2147483647;的型別改成long long試試看吧

#include <iostream>

int main()
{
    long long max_interger = 2147483647;
    std::cout << max_interger+1 << std::endl;
    return 0;
}

結果為2147483648,看起來突破了int最大整數的限制了

然而我們做一個小修改,將+1改寫到長整數的宣告中:

#include <iostream>

int main()
{
    long long max_interger = 2147483647+1;
    std::cout << max_interger << std::endl;
    return 0;
}

你會想結果應該還是要印出2147483648吧?
但結果卻印出負數的-2147483648
也就是int範圍的「最小整數」,
最初測試的「溢位」現象發生了

這問題其實跟本系列【魔鬼般的c++語法細節】(1) 5/2是2.5還是2?談的問題有點像,
那篇文章最後說道「運算符號的兩端是什麼類型,就會做什麼類型的運算」,
即是說當我們在做long long max_interger = 2147483647+1的計算時,
單純寫數字會讓程式以為說21474836471都是int
所以進行了int的加法,得到-2147483648
之後再把-2147483648這個數字轉型為long long

所以不要讓程式產生「溢位」現象的方法便是,
讓程式知道21474836471其中一個想代表的是long long即可

比如說像前一種寫法:

#include <iostream>

int main()
{
    long long max_interger = 2147483647;
    std::cout << max_interger+1 << std::endl;
    return 0;
}

這種寫法先宣告一個long long變數為2147483647
所以在做加法時,程式便清楚知道說是long long的加法運算,
不是int的加法運算

那如果一定要直接做long long max_interger = 2147483647+1;就必須要做「轉型」,
這邊提供兩種解決方法:

解法一、於數字前加上括號long long轉型

#include <iostream>

int main()
{
    long long max_interger = (long long)2147483647+1;
    std::cout << max_interger << std::endl;
    return 0;
}

解法二、於數字前加上後綴LL

第二個方法,在數字後面加上後綴LL,
也可以讓程式知道型態是long long
(註: 寫小寫的英文字母「ll」或大寫的「LL」程式都可以編譯,
但是如果要寫請養成好習慣,寫大寫的「LL」,
因為小寫的英文字母「l」太容易誤看成數字的「1」了)

#include <iostream>

int main()
{
    long long max_interger = 2147483647LL+1;
    std::cout << max_interger << std::endl;
    return 0;
}

為什麼long long L = 10000000*10000000;會產生溢位呢?

那麼回歸標題「為什麼long long L = 10000000*10000000;會產生溢位呢?」,
我們執行底下這支程式

#include <iostream>

int main()
{
    long long L = 10000000*10000000;
    std::cout << L << std::endl;
    return 0;
}

會發現結果得到一個不合理的數字276447232
即是因為程式把10000000當做是一個int來計算,
而產生「溢位」現象了,
相信讀者應該也知道如何修正這個問題了

修正後的程式

#include <iostream>

int main()
{
    long long L = 10000000LL*10000000;
    std::cout << L << std::endl;
    return 0;
}

課後練習

小馬現在想用程式計算1+2+3+…+n的值,
n是一個大於1的int(保證n不會超過2147483647),
由使用者輸入n的值,
根據數學公式,1+2+3+…+n=n*(n+1)/2
所以小馬的程式是這樣寫的:

#include <iostream>

int main()
{
    int n;
    std::cin >> n;
    long long L = (long long)n*(n+1)/2;
    std::cout << L << std::endl;
    return 0;
}

但這支程式在輸入很大的n時好像會出錯(因為溢位),
你能幫忙修正嗎?

(注意n輸入2147483647的時候答案應為2305843008139952128)


尚未有邦友留言

立即登入留言