开发者

System.ArithmeticException not thrown in F#

开发者 https://www.devze.com 2023-02-06 04:47 出处:网络
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).

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号