2011-01-24 4 views
1

Рассмотрим следующий кодSystem.ArithmeticException не выброшен в F #

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

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

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

Любая идея, почему и как сделать исключение на NaN или Infinity?

ответ

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 } 

Это может быть слишком сложный механизм для простых проверок, но я считаю это довольно приятным. Определение расчетной строитель выглядит следующим образом (вам нужно добавить 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() 
+0

+1 Вычисления выражения еще раз сохраняют день. – Daniel

+0

Мне не нравится ваше решение, но я буду отмечать его как принятый ответ, потому что это, вероятно, единственный способ пойти, кроме обертывания поплавка. Но определенно есть возможность выбросить исключение на NaN, бесконечность для удвоений для целей отладки ... –

+0

Мне нравится 'Double.IsNaN (v) |> not' idiom. Не! – Gabe

3

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

От the MSDN docs:

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


Update: Если вы хотите исключения быть выброшен в случаях Infinity или NaN, я бы предложил same advice as desco и предложил вам обернуть методы, которые вы хотите вызвать.

К сожалению, я недостаточно знаком с F #, чтобы приводить примеры кода на выбранном вами языке; но в C# вы можете сделать это, например, для функции квадратного корня:

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: Еще один вариант должен были бы написать свою собственную обертку для самого double типа, который не позволяет Infinity или NaN значения (опять же, ниже C# -I прошу прощения, если это не представляется возможным в 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; 
    } 
} 

Тогда везде, где вы пишете код WHER e вы не хотите разрешать Infinity или NaN, используйте этот тип, а не double напрямую.

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

+0

В дополнение к этому достаточно кратким ответом, вот веб-страницы на вопрос, который мне показался интересным: http://kossovsky.net/index.php/2010/04/division-by-zero-is-simple-but-the-result-not/. –

+0

Таким образом, это означает, что нет простого способа проверить, существует ли NaN или Infinity в коде, правильно? IsInfinity не является вариантом, так как это означало бы проверять каждую строку кода, каждую операцию, каждый отдельный массив и т. Д. Ужасно ... –

1

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