您的位置:首页 > 编程语言 > C#

C#发生算术运算中发生溢出或下溢的解决方案

2016-12-01 16:40 447 查看

一、现象描述

    在调用一个C++ Builder写的dll之后,操作另外的一个2D Chart控件后,报如下错误:

Message :算术运算中发生溢出或下溢。, Source:System.Windows.Forms,    在 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)

   在 System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)

   在 System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)

   在 System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)

   在 System.Windows.Forms.Application.RunDialog(Form form)

   在 System.Windows.Forms.Form.ShowDialog(IWin32Window owner)
   在 System.Windows.Forms.Form.ShowDialog()

二、分析

    从异常现象来看,显然和浮点数的运算有关。因为dll和2D控件本身是没有源码的,无法获知到具体异常原因。因此一个自然而然的想法是,可能是dll中某些函数更改了浮点数的设置后没有复位,导致此异常出现。

    从网上资料看,确实也存在这种现象:

This solution is only for those guys who r working on different culture like Farsi, Urdu, Arabic and many other, When we change the culture in the .net frame work,
they call some unmanaged code(dll's) which change the floating point of the compiler and didn't set it back to its original floating point.So we change the calling sequence to set floating point back to its original floating point before transferring the control
to managed code. 

     为验证此想法,在调用dll中函数的前后,观察其浮点掩码的状态情况。

int  status1 = _controlfp(0, 0);
......//调用dll的代码
int status2 = _controlfp(0, 0);

    运行程序,获取到上述两个状态,得到:status1为0x0009001F,stauts2为0x000C0003。因此断定是组件dll更改了系统缺省的浮点掩码设置。

三、解决方案

    在VS中,有一个函数,用于设置浮点异常掩码,即_controlfp。其原型为:

unsigned int _controlfp(
unsigned int new,
unsigned int mask
);

参数:new,新的控制字位值。mask,设置新的控制字位的掩码。 

如果 
mask
 不为零,控制字的新值被设置:对于在 
mask
 中打开的位(即等于 1),在 
new
 中,对应的位用于更新控制字。 

换而言之,
fpcntrl
 
=
 (
fpcntrl
 
& ~mask
)|(
new & mask
) 其中 
fpcntrl
 是浮点控制字。

返回值:按位返回的值指示浮点控制态。  

    常用的mask如下:

掩码十六进制值常量十六进制值
_MCW_DN
 (不正常的控件)
0x03000000
_DN_SAVE


 
_DN_FLUSH
0x00000000

0x01000000
_MCW_EM
 (中断异常掩码)
0x0008001F
_EM_INVALID


 
_EM_DENORMAL


 
_EM_ZERODIVIDE


 
_EM_OVERFLOW


 
_EM_UNDERFLOW


 
_EM_INEXACT
0x00000010

0x00080000

0x00000008

0x00000004

0x00000002

0x00000001
_MCW_IC
 (无限制控件)

(不支持 ARM 或 x64 平台。)
0x00040000
_IC_AFFINE


 
_IC_PROJECTIVE
0x00040000

0x00000000
_MCW_RC
 (舍入控件)
0x00000300
_RC_CHOP


 
_RC_UP


 
_RC_DOWN


 
_RC_NEAR
0x00000300

0x00000200

0x00000100

0x00000000
_MCW_PC
 (精度控件)

(不支持 ARM 或 x64 平台。)
0x00030000
_PC_24
 (24 位)

 
_PC_53
 (53 位)

 
_PC_64
 (64 位)
0x00020000

0x00010000

0x00000000
    因此只需要在执行完毕dll的调用代码后,调用_controlfp来回复默认值即可达到目的。
    C#应用示例:    

[DllImport("msvcr70.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe public static extern int _controlfp(int n, int mask);
internal void ResetFPCR()
{
const int _EM_OVERFLOW = 0x0009001F;//为原来的默认值
const int _MCW_EM = 0x000FFFFF;//设置默认值相关的掩码为1,即可设置成默认值。
_controlfp(_EM_OVERFLOW, _MCW_EM);
}
void Test()
{
......//调用dll的代码
ResetFPCR();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  开发者 c# 解决方案
相关文章推荐