当任何数学运算在.net 4中产生"NaN"时,如何强制C#编译器引发异常? [英] How do I force the C# compiler to throw an exception when any math operation produces 'NaN' in .net 4?

查看:53
本文介绍了当任何数学运算在.net 4中产生"NaN"时,如何强制C#编译器引发异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个很长的复杂源代码,我需要找到将变量值设置为nan的确切位置.因此,我需要编译器在此时抛出异常. 这个问题已经被问过了.我发现以下答案是一个很好的答案.这段代码在.net 3.5中运行良好,但是当我使用.net 4时,此解决方案无法正常工作.即使在调试器中启用了引发异常时中断",该异常也无法位于代码中.有什么主意吗?

I have a long complicated source code and I need to find the exact location at which a variable value set to nan. So I need the compiler to throw an exception at that point. This question has been asked before. I found the following answer as a good answer. This code works well in .net 3.5.But when I use .net 4, this solution doesn't work correctly. Even when I Enable "break when an exception is thrown" in the debugger, the exception can't be located in the code. Any idea?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace ConsoleApplication2
{
  class Program
  {
    [System.Runtime.InteropServices.DllImport("msvcrt.dll")]
    public static extern uint _control87(uint a, uint b);

    [System.Runtime.InteropServices.DllImport("msvcrt.dll")]
    public static extern uint _clearfp();

    static void Main(string[] args)
    {
      float zero = 0.0f - args.Length; // Want 0.0f. Fool compiler...
      System.Console.WriteLine("zero = " + zero.ToString());

      // A NaN which does not throw exception
      float firstNaN = zero / 0.0f;
      System.Console.WriteLine("firstNaN= " + firstNaN.ToString());

      // Now turn on floating-point exceptions
      uint empty = 0;
      uint cw = _control87(empty, empty); // Debugger halts on this one and complains about false signature, but continue works.
      System.Console.WriteLine(cw.ToString());
      uint MCW_EM = 0x0008001f; // From float.h
      uint _EM_INVALID = 0x00000010; // From float.h (invalid corresponds to NaN
      // See http://www.fortran-2000.com/ArnaudRecipes/CompilerTricks.html#x86_FP

      cw &= ~(_EM_INVALID);
      _clearfp(); // Clear floating point error word.
      _control87(cw, MCW_EM); // Debugger halts on this one and complains about false signature, but continue works.      
      System.Console.WriteLine(cw.ToString());

      // A NaN which does throw exception
      float secondNaN = 0;
      try
      {
        // Put as much code here as you like.
        // Enable "break when an exception is thrown" in the debugger
        // for system exceptions to get to the line where it is thrown 
        // before catching it below.
        secondNaN = zero / 0.0f;
      }
      catch (System.Exception ex)
      {
        _clearfp(); // Clear floating point error word.
      }      

      System.Console.WriteLine("secondNaN= " + secondNaN.ToString());
    }
  }
}

推荐答案

发现了问题.修改呼叫以使用_controlfp代替_control87,然后将DllImport更改为:

Found the problem. Modify the calls to use _controlfp instead of _control87, and then change the DllImport to:

[System.Runtime.InteropServices.DllImport("msvcrt.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
public static extern uint _controlfp(uint a, uint b);

[System.Runtime.InteropServices.DllImport("msvcrt.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
public static extern uint _clearfp();

您必须明确地告诉.NET,必须使用Cdecl约定来调用您的方法.然后就可以了.下次,请写完整的例外...完整的消息应该是:

You have to explicitly tell .NET that your methods must be called with the Cdecl convention. Then it works. Next time please write the full exception... The full message should be:

对PInvoke函数'_controlfp'的调用已使堆栈不平衡.这可能是因为托管PInvoke签名与非托管目标签名不匹配.检查PInvoke签名的调用约定和参数是否与目标非托管签名匹配.

A call to PInvoke function '_controlfp' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.

或者您可以作弊:-)

struct DoubleNoNan
{
    public readonly double Value;

    public DoubleNoNan(double value)
    {
        if (Double.IsNaN(value))
        {
            throw new Exception("NaN");
        }

        this.Value = value;
    }

    public static implicit operator double(DoubleNoNan value)
    {
        return value.Value;
    }

    public static implicit operator DoubleNoNan(double value)
    {
        return new DoubleNoNan(value);
    }

    public override bool Equals(object obj)
    {
        return this.Value.Equals(obj);
    }

    public override int GetHashCode()
    {
        return this.Value.GetHashCode();
    }

    public override string ToString()
    {
        return this.Value.ToString();
    }
}

然后:

DoubleNoNan dn = 0.0;
dn = / 0.0;

对于float,将Double替换为Single,将double替换为float.运算符将由隐式强制转换提供.

For the float, replace the word Double with Single and the word double with float. Operators will be provided by the implicit casts.

显然它有其局限性(不能在ref/out表达式以及许多其他表达式中使用)

Clearly it has its limits (it can't be used in ref/out expressions, and many others)

您的代码似乎可以正常工作...我已经以32位和64位格式对其进行了检查

Your code seems to work correctly... I've checked it at 32 and 64 bits

这篇关于当任何数学运算在.net 4中产生"NaN"时,如何强制C#编译器引发异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆