溢出
就是結果值超出了被賦值對象的類型長度。
例如 8 位(1字節)的 char 。根據它有符號還是無符號, 它可以包含最大數127 或 255 。
下面的乘法向一個 char 賦值 256 ,因而導致了溢出:
int main()
{
char value = 32;
int ival = 8;
value = ival * value;
//cout << "value: " <<value << endl;
cout << "value: " << static_cast<int>(value) << endl;
}
表示256需要9位,因而向value賦值256導致了與其相廣聯的內存的溢出。value包含的實際值是未定義的,所以在執行時就可能會引起問題。
當用表達式:
cout << "value: " << value << endl;
試圖輸出它時,程序輸出結果如下:
想了一會後,意識到,在ASCIL碼集中,0代表空(NULL)字符,所以什麼也不會輸出。如下特殊表達式:
cout << "value: " << static_cast<int>(value) << endl;
這種情況下,我們吧value轉換位一個int類型的對象。這時候程序輸出:
在本例中,我們改變的不是value相關的值,而是它被輸出操作符解釋的方式。當它被當作char型時,值會被映射到相關聯的ASCIL表上,輸出對應的字符;當被當作int時,會直接輸出它的值。
這裏順便補充一下,C++支持的四種類型強制轉換:
- const_cast:去掉對象的const屬性的
- static:編譯器認爲可以支持的強轉方式,安全性略高
- reinterpret_const:類似於C的強轉
- dynamic_cast:PTTI強制類型轉換(run-time type information)
最近經別人介紹看到了《C標準》(The Standard C Library)這本書,(網上很多需要付費下載,找了好久資源的)
在這本書的limits那章提到了與內置類型表示有關的信息,例如一個類型能表示的最大值和最小值,以及怎樣使用這些頭文件防止溢出(overflow)和下溢(underflow)。
1.在下面給出的值會被適合在#if預處理指令中使用的常量表達式代替。
例如,假設在要表示某個值在VAL_MIN和VAL_MAX之間的有符號數據,就可以通過以下代碼來防止程序出現翻譯錯誤:
#include <assert.h>
#include <limits.h>
#if VAL_MIN < INT_MIN || INT_MAX < VAL_MAX
#error values out of range
#endif
使用
#include <assert.h>
#include <limits.h>
#if VAL_MIN < INT_MIN || INT_MAX < VAL_MAX
typedef long Val_t;
#else
typedef int Val_t;
#endif
然後就可以把所有在這個範圍中變化的數據對象聲明爲Val_t類型。程序就會選擇效率更高的類型。
這裏呢,
/*HANDLE OVERFLOW*/
if(x < log(DBL_MAX))
y = exp(x);
else
...
可以通過使用一個相關的宏來避免計算log(DBL_MAX),就像:
/*HANDLE OVERFLOW*/
if (x <= PLT_MAX_10_EXP)
y = pow(10, x);
else
...
(2)下溢
爲了避免下溢,一定要保證所有的值都大於DEL_MIN的數值。下溢的結果不像上溢那樣損失慘重,但是仍然麻煩很大。IEEE 754浮點算術提供了漸進下溢,減輕了下溢的一些最壞影響。很多浮點的實現都用零值代替一個過小而不能表示的值。這會在除以一個會產生下溢的值是,遇到麻煩。
可以作如下相應的測試:
/*HANDLE UNDERFLOW*/
if(log(DBL_MIN) <= x)
y = exp(x);
else
...
if(FLT_MIN10_EXP <= x)
y = pow(10, x);
else
...
if(FLT_MIN_EXP < n)
y = ldexp(1.0, n);
else
...
(3)有效值丟失
當對兩個幾乎相等的值相減是就會發生有效值丟失。
一個可以阻止有效值丟失的方法——把一個很小的書和一個很大的數相加。
在加的過程中,較小的數的重要作用就可能體現不出來。
參考資料:
《C++ Primer 3rd Edition》
《C++標準》