天天看點

捕獲數學函數異常

捕獲數學函數異常

作者: 江漢石油學院計算機系 周雲才

下載下傳本文配套源代碼

假如我們要用一個數學函數,比如反正弦函數asin(x),如果變元x的值是由使用者提供或某個中間結果,則在調用時必須判斷其取值範圍是合理,是否滿|x|<=1?即

if(fabs(x)<=1)
   y=asin(x);
else
   y=…      

對數函數也可作類似的處理。但是如果遇到幂函數pow(x,y)時,問題就不那麼簡單了。仔細分析将發現:

             y

   x  

負小數 負整數 整數 小數
負小數 無意義 有意義 有意義 有意義 無意義
負整數 無意義 有意義 有意義 有意義 無意義
無意義 無意義 有意義 有意義 有意義
整數 有意義 有意義 有意義 有意義 有意義
小數 有意義 有意義 有意義 有意義 有意義

例如:pow(-1.2,-1.2)=-1.#IND。如果要程式設計處理,至少需要六個if語句。即使如此,也有麻煩:如何判斷一個double型的變元的值是整數還是小數?

為了處理數學函數運算中出現的異常,VC++提供了一個函數_mather,其原型在<math.h>中:

int _matherr( struct _exception *except );      

為了利用此函數,隻需在應用數學函數的地方定義一個這樣的函數,例如

#include <math.h>
#include <stdio.h>
void main()
{
	double x,y,z;
	x=-1.23;
	y=-1;
	z=pow(x,y);
	printf("%g/n",z);
	y=-1.1;
	z=pow(x,y);
	printf("%g/n",z);
}

int _matherr(struct _exception *except)
{
char* errorString[] = {"_DOMAIN","_SING", "_OVERFLOW", "_PLOSS", 
"_TLOSS", "_UNDERFLOW"};
	printf("Error function name is %s/n",except->name);
	printf("The varianbles arg1=%g,arg2=%g/n",except->arg1,except->arg2);
	printf("The error type = %s/n",errorString[except->type]);
	printf("The error value=%g/n",except->retval);
	except->retval=1234;
	printf("After handling error value=%g/n",except->retval);
	return 1;
}
      

編譯、運作,結果為

-0.813008

Error function name is pow

The varianbles arg1=-1.23,arg2=-1.1

The error type = _SING

The error value=-1.#IND

After handling error value=1234

1234

Press any key to continue

第一行為-1.23的倒數,第二~六兩行是_matherr函數的輸出,第七行是主函數的輸出。

也許有人會說,main函數并沒有調用_matherr函數,為什麼會出現這種情況呢?這就是VC++編譯器為我們做的事情了。它很有可能在數學函數中設定了跳轉來實作異常處理,當數學庫中的符點函數探測到一個錯誤時,就調用此函數。下面是有關_matherr函數的一些說明:

1、傳回值:類型是整型的。按慣例,0傳回值用來标志一個錯誤,非0值标志成功。如果傳回0,則錯誤資訊可被顯示,錯誤序号被正确設定。如果傳回非0值,沒有顯示錯誤資訊,錯誤序号也保持不變。

2、參數:except指針指向一個包含錯誤資訊的結構 struct _exception。

_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 <math.h>
#include <string.h>
#include <stdio.h>

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

作者資訊:

姓名:周雲才

郵箱:[email protected] 

聯系位址:江漢石油學院計算機系 郵編 434023