数学函数的异常捕获——Ada应用实例之十
2010-05-18 18:00
543 查看
一帖子报告用C++语言编写了龙格——库塔方法解一个状态方程的程序,编译系统是Borland C++。程序运行过程中出现异常,错误信息是“floating point error”。
笔者也用Borland C++试了一下,确实出错,但DEBUG状态下停在一条fprintf语句处,这里显然不是发生异常的地方。估计是浮点运算出错。但程序中有大量的算术运算和数学函数调用,很难通过单步运行来定位错误。
首先怀疑数学函数调用。这可通过调用C++数学库中的函数_matherr来发现数学函数调用的异常。该函数的原型在<math.h>中:
int _matherr( struct _exception *except );
为了利用此函数,只需在程序中定义一个这样的函数,例如:
int _matherr(struct _exception *except)
{
const char* errorString[] =
{
"", /*异常类型从1开始编号,因此[0]留空*/
"_DOMAIN(参数域错误)",
"_SING(参数奇异)",
"_OVERFLOW(上溢)",
"_UNDERFLOW(下溢)",
"_TLOSS(丢失全部精度)",
"_PLOSS(丢失部分精度)"
};
printf("出现异常的数学函数名称:%s/n",except->name);
printf("该函数的输入参数:参数1=%g, 参数2(如果有)=%g/n",except->arg1,except->arg2);
printf("异常类型:%s/n",errorString[except->type]);
printf("该函数的返回值:%g/n",except->retval);
return 1;
}
然后在这个函数中设了一个断点。运行时果然进入了这个函数,原来是 sqrt的参数是负数,所以出现异常。
如果用Ada编程,那么事情就简单多了。设有以下程序:
with Ada.Exceptions; use Ada.Exceptions;
with Ada.Numerics.Elementary_Functions;
use Ada.Numerics.Elementary_Functions;
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
r : Float;
begin
r := Sqrt (-1.0);
exception
when Event : others =>
Put ("Unexpected exception:");
Put_Line (Exception_Name (Event));
Put_Line (Exception_Message (Event));
end Main;
上述程序运行后将输出如下信息:
Unexpected exception:ADA.NUMERICS.ARGUMENT_ERROR
Exception name: ADA.NUMERICS.ARGUMENT_ERROR
Message: a-ngelfu.adb:933 instantiated at a-nuelfu.ads:18
最后一行指出异常发生的源文件位置。文件a-ngelfu.adb是数学库的源文件,在933行附近的代码是:
function Sqrt (X : Float_Type'Base) return Float_Type'Base is
begin
if X < 0.0 then
raise Argument_Error; -- 933行
-- Special case Sqrt (0.0) to preserve possible minus sign per IEEE
elsif X = 0.0 then
return X;
end if;
return Float_Type'Base (Aux.Sqrt (Double (X)));
end Sqrt;
由此可见,异常原因是sqrt的输入参数是负数。
笔者也用Borland C++试了一下,确实出错,但DEBUG状态下停在一条fprintf语句处,这里显然不是发生异常的地方。估计是浮点运算出错。但程序中有大量的算术运算和数学函数调用,很难通过单步运行来定位错误。
首先怀疑数学函数调用。这可通过调用C++数学库中的函数_matherr来发现数学函数调用的异常。该函数的原型在<math.h>中:
int _matherr( struct _exception *except );
为了利用此函数,只需在程序中定义一个这样的函数,例如:
int _matherr(struct _exception *except)
{
const char* errorString[] =
{
"", /*异常类型从1开始编号,因此[0]留空*/
"_DOMAIN(参数域错误)",
"_SING(参数奇异)",
"_OVERFLOW(上溢)",
"_UNDERFLOW(下溢)",
"_TLOSS(丢失全部精度)",
"_PLOSS(丢失部分精度)"
};
printf("出现异常的数学函数名称:%s/n",except->name);
printf("该函数的输入参数:参数1=%g, 参数2(如果有)=%g/n",except->arg1,except->arg2);
printf("异常类型:%s/n",errorString[except->type]);
printf("该函数的返回值:%g/n",except->retval);
return 1;
}
然后在这个函数中设了一个断点。运行时果然进入了这个函数,原来是 sqrt的参数是负数,所以出现异常。
如果用Ada编程,那么事情就简单多了。设有以下程序:
with Ada.Exceptions; use Ada.Exceptions;
with Ada.Numerics.Elementary_Functions;
use Ada.Numerics.Elementary_Functions;
with Ada.Text_IO; use Ada.Text_IO;
procedure Main is
r : Float;
begin
r := Sqrt (-1.0);
exception
when Event : others =>
Put ("Unexpected exception:");
Put_Line (Exception_Name (Event));
Put_Line (Exception_Message (Event));
end Main;
上述程序运行后将输出如下信息:
Unexpected exception:ADA.NUMERICS.ARGUMENT_ERROR
Exception name: ADA.NUMERICS.ARGUMENT_ERROR
Message: a-ngelfu.adb:933 instantiated at a-nuelfu.ads:18
最后一行指出异常发生的源文件位置。文件a-ngelfu.adb是数学库的源文件,在933行附近的代码是:
function Sqrt (X : Float_Type'Base) return Float_Type'Base is
begin
if X < 0.0 then
raise Argument_Error; -- 933行
-- Special case Sqrt (0.0) to preserve possible minus sign per IEEE
elsif X = 0.0 then
return X;
end if;
return Float_Type'Base (Aux.Sqrt (Double (X)));
end Sqrt;
由此可见,异常原因是sqrt的输入参数是负数。
相关文章推荐
- .NET 异常捕获机制的小技巧应用(实例:端口扫描程序)
- android 应用 捕获异常 重启应用
- 稳定婚姻的求解程序——Ada应用实例之一
- android 捕获应用异常并保存入日志文件中
- php中try catch捕获异常实例详解
- android全局捕获异常,重启应用
- iOS已发布应用中对异常信息捕获和处理
- C# 应用异常捕获
- iOS已发布应用中对异常信息捕获和处理
- Android应用捕获全局异常自定义处理
- 错误-终止应用程序由于未捕获的异常的nsinvalidargumentexception’,原因:“[:]:未知的UIView setImage选择器送到实例0x8d78d20”
- Android全局异常捕获,不退出应用,让应用正常运行下去!
- Android小技巧(一):实现捕获应用的运行时异常
- 嵌套循环的控制变量——Ada应用实例之十三
- iOS已发布应用中对异常信息捕获和处理
- iOS已发布应用中对异常信息捕获和处理 推荐
- Android 之 应用未捕获异常处理
- WF-异常捕获与补偿的应用说明
- 在C#代码中应用Log4Net(四)在Winform和Web中捕获全局异常
- python try except 捕获所有异常的实例