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());
}
}
}
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();
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:
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.
Or you can cheat :-)
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();
}
}
and then:
DoubleNoNan dn = 0.0;
dn = / 0.0;
For the float
, replace the word Double
with Single
and the word double
with float
. Operators will be provided by the implicit casts.
Clearly it has its limits (it can't be used in ref
/out
expressions, and many others)
Your code seems to work correctly... I've checked it at 32 and 64 bits
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With