System.ArithmeticException not thrown in F#
Consider the following code
let a = 5. / 0.
let b = sin a
let c = sqrt(-5.)
It produces both Infinity and NaN. In both cases I want to have an exception thrown (for debuging purposes).
I use Visual Studio 2010. I set up Debug/Exceptions.../Common Language Runtime Exceptions/Syst开发者_开发问答em/System.ArithmeticException to "Thrown", but when running the code, no exception is thrown.
Any idea why and how to throw an exception on NaN or Infinity?
As others noted, you'll have to check for the NaN condition explicitly. If you wanted to do this using some advanced F# features, then you could also use computation expressions.
It is possible to define a builder that automatically checks for Nan when you bind value using let!
. Then you could write something like:
check { let! a = 5. / 0. // 'a' is checked here
let! b = sin a // 'b' is checked here
let c = sqrt(-5.) // 'c' is not checked (no 'let!')
return a, b, c }
This may be too complicated mechanism for just simple checks, but I find it quite nice. The definition of the computation builder looks like this (you'll need to add While
, For
and some others to support all language constructs):
open System
type CheckedBuilder() =
member x.Bind(v:float, f) =
if Double.IsNaN(v) |> not then f v
else raise (new ArithmeticException())
member x.Return(v) = v
let check = CheckedBuilder()
If you want an arithmetic exception, try dividing an integer by zero. The System.Double
type (float
in F#) by design does not throw exceptions (all exceptional circumstances end up at NaN
).
From the MSDN docs:
The floating-point operators, including the assignment operators, do not throw exceptions. Instead, in exceptional situations the result of a floating-point operation is zero, infinity, or NaN....
Update: If you want exceptions to be thrown in cases of Infinity
or NaN
, I would offer the same advice as desco and suggest you wrap the methods you want to call.
Unfortunately, I'm not familiar enough with F# to give code examples in your language of choice; but in C# you might do this for example for a sqrt function:
public static double CheckedSqrt(double x)
{
double sqrt = Math.Sqrt(x);
if (double.IsNaN(sqrt))
{
throw new ArithmeticException("The square root of " + x + " is NaN.");
}
return sqrt;
}
Update 2: Yet another option would be to write your own wrapper for the double
type itself which does not allow Infinity
or NaN
values (again, the below is C#—I apologize if this isn't possible in F# in which case I'm giving you absolutely useless advice):
public struct CheckedDouble // : IEquatable<CheckedDouble>, etc.
{
double m_value;
public CheckedDouble(double value)
{
if (double.IsInfinity(value) || double.IsNaN(value))
{
throw new ArithmeticException("A calculation resulted in infinity or NaN.");
}
m_value = value;
}
public static implicit operator CheckedDouble(double value)
{
return new CheckedDouble(value);
}
public static implicit operator double(CheckedDouble checkedDouble)
{
return checkedDouble.m_value;
}
}
Then wherever you're writing code where you don't want to allow Infinity
or NaN
, use this type rather than double
directly.
Just another option.
think, this is possible only by providing your custom wrappers over sin\sqrt. Current behavior is documented for Math.Sqrt and Math.Sin.
精彩评论