1

考虑以下代码

let a = 5. / 0.
let b = sin a
let c = sqrt(-5.)

它同时产生 Infinity 和 NaN。在这两种情况下,我都希望抛出异常(出于调试目的)。

我使用 Visual Studio 2010。我将 Debug/Exceptions.../Common Language Runtime Exceptions/System/System.ArithmeticException 设置为“Thrown”,但是在运行代码时,没有抛出异常。

知道为什么以及如何在 NaN 或 Infinity 上引发异常吗?

4

3 回答 3

5

正如其他人指出的那样,您必须明确检查 NaN 条件。如果您想使用一些高级 F# 功能来执行此操作,那么您还可以使用计算表达式。

可以定义一个构建器,当您使用绑定值时自动检查 Nan let!。然后你可以写这样的东西:

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 }

对于简单的检查来说,这可能太复杂了,但我觉得它很好。计算构建器的定义如下所示(您需要添加WhileFor其他一些以支持所有语言构造):

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()
于 2011-01-24T15:40:00.170 回答
3

如果您想要算术异常,请尝试将整数除以零。设计的System.Double类型(float在 F# 中)不会引发异常(所有异常情况都以 结束NaN)。

MSDN 文档

浮点运算符(包括赋值运算符)不会引发异常。相反,在特殊情况下,浮点运算的结果是零、无穷大或 NaN....


更新Infinity:如果您希望在or的情况下引发异常NaN,我会提供与 desco 相同的建议,并建议您包装您要调用的方法。

不幸的是,我对 F# 不够熟悉,无法以您选择的语言提供代码示例;但在 C# 中,您可能会为 sqrt 函数执行此操作:

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;
}

更新 2:另一种选择是为double不允许InfinityNaN值的类型本身编写自己的包装器(再次,下面是 C#——如果这在 F# 中不可能,我很抱歉,在这种情况下,我绝对给你无用的建议):

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;
    }
}

然后,无论您在哪里编写不想允许的代码,Infinity或者NaN,使用这种类型而不是double直接使用。

只是另一种选择。

于 2011-01-24T14:12:45.363 回答
1

想一想,这只能通过在 sin\sqrt 上提供自定义包装器来实现。记录了Math.SqrtMath.Sin的当前行为。

于 2011-01-24T14:14:06.940 回答