[英]System.ArithmeticException not thrown in F#
考慮以下代碼
let a = 5. / 0.
let b = sin a
let c = sqrt(-5.)
它同時產生Infinity和NaN。 在這兩種情況下,我都希望引發一個異常(出於調試目的)。
我使用Visual Studio2010。我將Debug / Exceptions / Common Language Runtime Exceptions / System / System.ArithmeticException設置為“ Thrown”,但是在運行代碼時不會引發任何異常。
知道為什么以及如何在NaN或Infinity上引發異常嗎?
如其他人所述,您必須顯式檢查NaN條件。 如果您想使用某些高級F#功能來執行此操作,則還可以使用計算表達式。
可以定義一個構建器,該構建器在使用let!
綁定值時自動檢查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()
如果要算術異常,請嘗試將整數除以零。 System.Double
類型(按F# float
)在設計上不會引發異常(所有異常情況最終都以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
。
只是另一個選擇。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.