昨天遇到了一個關於浮點數計算溢出處理的問題,本來想用if語句把條件寫好。
可是遇到了很多困難,上網搜了一下,發現一個解決辦法:
if語句把條件寫好可以,但是如果遇到冪函數pow(x,y)時,問題就不那么簡單了。仔細分析將發現:
y
x
負小數
負整數
0
整數
小數
負小數
無意義
有意義
有意義
有意義
無意義
負整數
無意義
有意義
有意義
有意義
無意義
0
無意義
無意義
有意義
有意義
有意義
整數
有意義
有意義
有意義
有意義
有意義
小數
有意義
有意義
有意義
有意義
有意義
例如:pow(-1.2,-1.2)=-1.#IND。如果要編程處理,至少需要六個if語句。即使如此,也有麻煩:如何判斷一個double型的變元的值是整數還是小數?
為了處理數學函數運算中出現的異常,VC++提供了一個函數_mather,其原型在中:
--------------------------------------------------------------------------------------------------------------
所在頭文件:
函數原型:int _matherr( struct _exception *except );
函數功能:VC++自動調用此函數來判斷處理數學函數中出現的異常(不能手動調用)
函數返回值:0返回值用來標志一個錯誤,非0值標志成功。如果返回0,則錯誤信息可被顯示,錯誤序號被正確設置。如果返回非0值,沒有顯示錯誤信息,錯誤序號也保持不變。
函數參數:_exception結構包含有如下數據成員:
struct _exception
{
int type 異常類型;
char *name 出錯函數名;
double arg1, arg2 函數的第一和第二(如果有的話)參數;
double retval 函數的返回值。
}
-------------------------------------------------------------------------------------------------------------
數學函數的錯誤類型定義如下:
_DOMAIN 變元定義域錯誤;
_SING 變元奇異點錯誤;
_OVERFLOW 溢出錯誤;
_PLOSS 精度部分遺失;
_TLOSS 精度丟失;
_UNDERFLOW 下溢錯誤,結果太小,無發表示。
下面是MSDN給我們提供的一個示例供大家參考:
/* MATHERR.C illustrates writing an error routine for math
* functions. The error function must be:
* _matherr
*/
#include
#include
#include
void main()
{
/* Do several math operations that cause errors. The _matherr
* routine handles _DOMAIN errors, but lets the system handle
* other errors normally.
*/
printf( "log( -2.0 ) = %e\n", log( -2.0 ) );
printf( "log10( -5.0 ) = %e\n", log10( -5.0 ) );
printf( "log( 0.0 ) = %e\n", log( 0.0 ) );
}
/* Handle several math errors caused by passing a negative argument
* to log or log10 (_DOMAIN errors). When this happens, _matherr
* returns the natural or base-10 logarithm of the absolute value
* of the argument and suppresses the usual error message.
*/
int _matherr( struct _exception *except )
{
/* Handle _DOMAIN errors for log or log10. */
if( except->type == _DOMAIN )
{
if( strcmp( except->name, "log" ) == 0 )
{
except->retval = log( -(except->arg1) );
printf( "Special: using absolute value: %s: _DOMAIN "
"error\n", except->name );
return 1;
}
else if( strcmp( except->name, "log10" ) == 0 )
{
except->retval = log10( -(except->arg1) );
printf( "Special: using absolute value: %s: _DOMAIN "
"error\n", except->name );
return 1;
}
}
else
{
printf( "Normal: " );
return 0; /* Else use the default actions */
}
}
輸出結果
Special: using absolute value: log: _DOMAIN error
log( -2.0 ) = 6.931472e-001
Special: using absolute value: log10: _DOMAIN error
log10( -5.0 ) = 6.989700e-001
Normal: log( 0.0 ) = -1.#INF00e+000
main函數並沒有調用_matherr函數,為什么會出現這種情況呢?這就是VC++編譯器為我們做的事情了。它很有可能在數學函數中設置了跳轉來實現異常處理,當數學庫中的符點函數探測到一個錯誤時,就調用此函數。
以上是網上的解決辦法,我自己也想了一下,這樣做的確是比用一般if語句判斷好多了,可是這個是系統自動調用的,在什么地方調用不知道,什么時候調用也不知道,不透明,任何的錯誤都在函數中處理,有時候也不方便。
后來經過不斷試驗,我發現,float 和double類型的數據,或是各種數學函數,在發生溢出或錯誤的時候會返回一個異常浮點數,只要我們判斷出這個異常,就可以進一步處理,這樣的處理了方式相對就自由多了。
上網找了一些資料:
常見異常的浮點數有:
1.#INF:這個值表示“無窮大inf (infinity 的縮寫)”,即超出了計算機可以表示的浮點數的最大范圍(或者說超過了 double 類型的最大值)。例如,當一個整數除以0時便會得到一個1.#INF / inf值;相應的,如果一個負整數除以0會得到 -1.#INF / -inf 值。
1.#IND:這個的情況更復雜,一般來說,它們來自於任何未定義結果(非法)的浮點數運算。"IND"是 indeterminate 的縮寫,而"nan"是 not a number 的縮寫。產生這個值的常見例子有:對負數開平方,對負數取對數,0.0/0.0,0.0*∞, ∞/∞ 等。
簡而言之,如果遇到 1.#INF / inf,就檢查是否發生了運算結果溢出除零,而遇到 1.#IND / nan,就檢查是否發生了非法的運算。
在vc中,可以用float.h中的,用一下函數來判斷:
int _isnan(double x) 判斷x是不是無效數據(NAN),是返回1,不是返回0
int _finite(double x)判斷x是不是無窮大(1.#INF),是返回0,不是返回非零值
int _fpclass(double x)用於檢驗一個浮點數的類型。
_fpclass的返回值有:
_FPCLASS_SNAN /* signaling NaN */
_FPCLASS_QNAN /* quiet NaN */
_FPCLASS_NINF /* negative infinity */
_FPCLASS_NN /* negative normal */
_FPCLASS_ND /* negative denormal */
_FPCLASS_NZ /* -0 */
_FPCLASS_PZ /* +0 */
_FPCLASS_PD /* positive denormal */
_FPCLASS_PN /* positive normal */
_FPCLASS_PINF /* positive infinity */
到這里,只要調用函數,VC下浮點數溢出的問題,迎刃而解~~~~~
http://www.vckbase.com/document/viewdoc/?id=437
http://live.aulddays.com/tech/10/double-float-ind-nan-inf/
http://apple.wish365.net/program-not-a-number/
http://www.cppprog.com/2010/0105/190.html