System.ArithmeticException не генерируется в F # - PullRequest
1 голос
/ 24 января 2011

Рассмотрим следующий код

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

Это производит и Бесконечность и NaN. В обоих случаях я хочу получить исключение (для целей отладки).

Я использую Visual Studio 2010. Я настроил Отладку / Исключения ... / Исключения времени выполнения общего языка / System / System.ArithmeticException в значение «Брошено», но при запуске кода исключение не выдается.

Есть идеи, как и как вызвать исключение для NaN или Infinity?

Ответы [ 3 ]

5 голосов
/ 24 января 2011

Как уже отмечалось, вам придется явно проверять условие 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 }

Это может быть слишком сложный механизм для простых проверок, но я нахожу его довольно хорошим.Определение компоновщика вычислений выглядит следующим образом (вам нужно добавить While, For и некоторые другие для поддержки всех языковых конструкций):

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()
3 голосов
/ 24 января 2011

Если вы хотите получить арифметическое исключение, попробуйте разделить целое число на ноль.Тип System.Double (float в F #) по конструкции не создает исключений (все исключительные обстоятельства заканчиваются на NaN).

С документы MSDN :

Операторы с плавающей запятой, включая операторы присваивания, не генерируют исключения.Вместо этого в исключительных ситуациях результатом операции с плавающей запятой является ноль, бесконечность или NaN ....


Обновление : если вы хотите, чтобы исключения былиброшенный в случаях Infinity или 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введите сам, который не позволяет значения Infinity или NaN (опять же, ниже 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 напрямую.

Просто еще один вариант.

1 голос
/ 24 января 2011

думаю, это возможно, только если вы предоставите свои пользовательские оболочки через sin \ sqrt. Текущее поведение задокументировано для Math.Sqrt и Math.Sin .

...