在C++中,每一種內置的數據類型都擁有不同的屬性,其中包含的信息對設計程序來說是非常重要的,下面來看一下,<limits>庫是怎樣有助於訪問這些信息的。
C++中約有10種截然不同的整數類型及超過3種的浮點類型,而每種數據類型都有不同的數值屬性,如數值范圍、能表示的最大位數、或各自的精度等等,這些屬性對金融、科學、圖形、數字信號處理等程序來說是極其重要的。本文討論使用<limits>庫,怎樣在程序中獲得這些基本數據類型的數值屬性。
"一個double類型中能存儲多少位?","signed long能表示的最大正數是多少?"如果這些問題的答案對你的程序很重要,那麼你怎樣以一種方便、且系統的方法來得到答案呢?答案就是:使用標准<limits>庫。
浮點的樂章
C++中浮點數據類型精度是有限的,某些與硬件有關的特性導致了浮點數據類型的截斷與取整。現在,你就明白為什麼2.0/3.0的結果大概是0.66666666666666663了吧,"數字噪音"通常是大多數bug的源頭,請看如下例子:
double d1=2., d2=3.;
d1/=d2; // 2/3
if (d1*10==(20./d2)) //條件本應該是"真"的,但,哎!
{
//永遠不可能執行到的代碼
do_equal();
}
花括號中的代碼行永遠也不可能執行,因為在 == 兩邊的表達式結果會有輕微的差別,d1*10的結果是6.6666666666666661,而20./d2的結果是6.6666666666666670,正是這種浮點算法的截斷與近似值導致了此差異的發生。在此,可使用定標整數,但有時這並不是一個妥善的解決辦法,試想有一張計算復數公式的電子表格--它必須使用浮點類型,在這種情況下,小正數(epsilon)常量這個問題就來了,小正數通常為可用給定數據類型的大於1的最小值與1之差來表示。舉例來說,double類型的小正數為:
#include <iostream>
#include <limits>
using namespace std;
cout << numeric_limits<double>::epsilon( ) << endl; //輸出:2.22045e-016
為減少if語句中數字噪音帶來的影響,可用一個檢查兩值粗略相等的表達式來代替 == 操作符。如:
if ( ((d1*10)-(20.0/d2)) <= numeric_limits<double>::epsilon())
{
do_equal();
}
如果double類型的(d1*10)-(20.0/d2)結果不大於小正數,那麼它幾乎為零,因此,兩個子表達式結果相等,應用此技巧可有效降低錯誤的閥值。例如,如果十億分之一或者更小的數值,對你的程序來說無關緊要,那麼可試下以下的技巧:
const double BILLIONTH=1./1000000000;
if ( ((d1*10)-(20.0/d2)) <= BILLIONTH)
此處請記住,小正數是最小的偏差極限。
比double更好
選擇一種浮點數據類型的標准,是它可以在精度無損的情況下最大存儲的十進制位數。例如,假設你的程序必須支持到16位的十進制數,那麼應該使用double、long double還是用戶自定義類型呢?要解答此問題,可使用numeric_limits::digits10常量,它會告訴你在精度無損情況下某種類型可表示的最大十進制位數:
cout<<numeric_limits<double>::digits10<<endl;//輸出:15
看起來double並不支持這種精度,那麼long double呢?
cout<<numeric_limits<long double>::digits10<<endl; //輸出:18
對了,它就可以。請注意,digits10對整型數也同樣適用:
cout<<numeric_limits<long>::digits10<<endl; //輸出:9
最大值與最小值
最大值與最小值即是對相應類型調用numeric_limits::max()和numeric_limits:min()所得到的值:
cout<<numeric_limits<int>::max()<<endl;// 2147483647
無限的<limits>
在IEC 559規范實現中,浮點數據類型可表示為"不是一個數字"或NaN。NaN是一種特殊的編碼,其代表某種非法數字,可由非法指令產生,或意為指示一個不應被忽略的數值。如果出現在表達式中的NaN沒有發出一個"信號",則其為"安靜"狀態;否則,其為一個發"信號"的NaN。下面的例子檢查在目標平台上支持哪種NaN類型,並把NaN的值賦給一個變量:
double d=0;
if(numeric_limits<double>::has_quiet_NaN)
d=numeric_limits<double>::quiet_NaN();
else if (numeric_limits<double>::has_signaling_NaN)
d=numeric_limits<double>::signaling_NaN();
else cerr<<"NaN for double isn't supported";
無限在此是一種特殊的情況,其通常由被零除或其他操作產生。下例代碼檢查目標平台上是否定義了一種特殊的無限碼,並把此值賦給一個變量:
float f=0;
if(numeric_limits<float>::has_infinity)
f=numeric_limits<float>::infinity();
else cerr<<"infinity for float isn't supported";