.NET 7 會將新的數學相關泛型介面引入基類連結庫。 這些介面的可用性表示您可以將泛型類型或方法的類型參數限制為類似數字的類型。 此外,C# 11 和更新版本可讓您定義 static virtual
介面成員。 由於運算子必須宣告為 static
,因此這個新的 C# 功能可讓運算符在類似數位類型的新介面中宣告。
這些創新使您能夠以通用方式執行數學運算,換句話說,不需要知道您正在使用的具體類型。 例如,如果您想要撰寫一個將兩個數字相加的方法,先前,您必須為每個類型新增多載的方法(例如 static int Add(int first, int second)
和 static float Add(float first, float second)
)。 現在您可以撰寫單一泛型方法,其中 type 參數限制為類似數位的類型。 例如:
static T Add<T>(T left, T right)
where T : INumber<T>
{
return left + right;
}
在此方法中,類型參數 T
受限於實作新 INumber<TSelf> 介面的類型。
INumber<TSelf> 實作了 IAdditionOperators<TSelf,TOther,TResult> 介面,其中包含 + 運算子。 這可讓方法通用地加總兩個數字。 方法可以搭配任何 .NET 的內建數值類型使用,因為它們都已更新為在 .NET 7 中實作 INumber<TSelf>。
函式庫作者將受益於泛型數學介面,因為他們可以藉由移除「冗餘」多載來簡化其程式碼基礎。 其他開發人員會間接受益,因為他們取用的 API 可能會開始支援更多類型。
介面
這些介面的設計既精細到足以讓用戶基於它們定義自己的接口,同時也具備細粒度以便於使用。 就該程度而言,有一些核心數值介面,大部分的用戶都會與其互動,例如 INumber<TSelf> 和 IBinaryInteger<TSelf>。 更精細的介面,例如 IAdditionOperators<TSelf,TOther,TResult> 和 ITrigonometricFunctions<TSelf>,支持這些類型,並可供定義自己的領域特定數值介面的開發人員使用。
數值介面
本節描述 中的 System.Numerics 介面,描述類似數字的類型及其可用的功能。
介面名稱 | 說明 |
---|---|
IBinaryFloatingPointIeee754<TSelf> | 公開實作 IEEE 754 標準的 二進位 浮點類型1 通用 API。 |
IBinaryInteger<TSelf> | 公開二進位整數2通用的 API。 |
IBinaryNumber<TSelf> | 公開二進位數通用的 API。 |
IFloatingPoint<TSelf> | 公開浮點類型通用的 API。 |
IFloatingPointIeee754<TSelf> | 公開實作 IEEE 754 標準的浮點類型通用 API。 |
INumber<TSelf> | 公開與可比較數位類型通用的 API(實際上為「實際」數位網域)。 |
INumberBase<TSelf> | 公開所有數字類型通用的 API(實際上為「複數」數位域)。 |
ISignedNumber<TSelf> | 公開所有有號數字類型的共通 API(例如 NegativeOne 的概念)。 |
IUnsignedNumber<TSelf> | 公開所有未帶正負號數字類型的通用 API。 |
IAdditiveIdentity<TSelf,TResult> | 揭示(x + T.AdditiveIdentity) == x 的概念。 |
IMinMaxValue<TSelf> | 揭示T.MinValue 和T.MaxValue 的概念。 |
IMultiplicativeIdentity<TSelf,TResult> | 揭示(x * T.MultiplicativeIdentity) == x 的概念。 |
1二進位 浮點類型 為 Double (double
)、 Half和 Single (float
)。
2 二進位 整數類型 為 Byte (byte
)、Int16 (short
)、Int32 (int
)、Int64 (long
)、Int128、IntPtr (nint
)、SByte (sbyte
)、UInt16 (ushort
)、UInt32 (uint
)、UInt64 (ulong
)、UInt128 和 UIntPtr (nuint
)。
您最有可能直接使用的介面是 INumber<TSelf>,大致對應到 實 數。 如果類型實作這個介面,則表示值具有正負號(這包含 unsigned
視為正數的類型),而且可以與相同類型的其他值進行比較。
INumberBase<TSelf> 更進階的概念,例如 複 數和 虛 數,例如負數的平方根等。 已建立其他介面,例如 IFloatingPointIeee754<TSelf>,因為並非所有操作對所有數字類型都有意義。例如,計算數字的取整只對浮點數類型有意義。 在 .NET 基類連結庫中,浮點類型 Double 會 IFloatingPointIeee754<TSelf> 實作,但 Int32 不會實作。
數個介面也由各種其他類型實作,包括 Char、、DateOnly、、DateTimeDateTimeOffsetDecimal、Guid、 TimeOnly和 TimeSpan。
下表顯示每個介面所公開的一些核心 API。
介面 | API 名稱 | 說明 |
---|---|---|
IBinaryInteger<TSelf> | DivRem |
同時計算商數和餘數。 |
LeadingZeroCount |
計算二進位表示法中前置零位的數目。 | |
PopCount |
計算二進位表示法中的設定位數目。 | |
RotateLeft |
向左旋轉位,有時也稱為圓形左移位。 | |
RotateRight |
向右旋轉位元,有時也稱為圓形右移位。 | |
TrailingZeroCount |
計算二進位表示法中尾端零位的數目。 | |
IFloatingPoint<TSelf> | Ceiling |
將值四捨五入為正無限大。 +4.5 會變成 +5,而 -4.5 會變成 -4。 |
Floor |
將值向下取整為負無限大。 +4.5 會變成 +4,而 -4.5 會變成 -5。 | |
Round |
使用指定的四捨五入模式將值進行四捨五入。 | |
Truncate |
把值向零取整。 +4.5 會變成 +4,而 -4.5 會變成 -4。 | |
IFloatingPointIeee754<TSelf> | E |
取得表示類型的歐拉數的值。 |
Epsilon |
取得對於某種類型來說大於零的最小可表示值。 | |
NaN |
取得值,表示 NaN 型別。 |
|
NegativeInfinity |
取得值,表示 -Infinity 型別。 |
|
NegativeZero |
取得值,表示 -Zero 型別。 |
|
Pi |
取得值,表示 Pi 型別。 |
|
PositiveInfinity |
取得值,表示 +Infinity 型別。 |
|
Tau |
取得表示型別 Tau 的值(2 * Pi )。 |
|
(其他) | (實作函數 介面下所列的完整介面集。 | |
INumber<TSelf> | Clamp |
將值限制為不超過指定的最小值和最大值。 |
CopySign |
將指定值的符號設為與另一個指定值相同。 | |
Max |
傳回兩個值中的較大值,如果其中一個輸入為 NaN ,則傳NaN 回 。 |
|
MaxNumber |
傳回兩個值中的較大值,如果一個輸入為 NaN ,則傳回數位。 |
|
Min |
傳回兩個值的較小值,如果其中一個輸入為 NaN ,則傳NaN 回 。 |
|
MinNumber |
傳回兩個值的較小值,如果一個輸入為 NaN ,則傳回數位。 |
|
Sign |
針對負值傳回 -1,0 表示零,而正值則傳回 +1。 | |
INumberBase<TSelf> | One |
取得型別的值 1。 |
Radix |
取得型別的進位或基底。 Int32 會傳回 2。 Decimal 返回 10。 | |
Zero |
取得型別的值 0。 | |
CreateChecked |
建立一個值,如果輸入無法容納,則會拋出OverflowException 。1 | |
CreateSaturating |
若輸入無法容納,則創建一個值,並將其限制在T.MinValue 和T.MaxValue 之間。1 |
|
CreateTruncating |
從另一個值建立一個新值,如果輸入無法容納,則會循環回到開始的值。1 | |
IsComplexNumber |
如果值具有非零實數部分和非零虛數部分,則傳回 true。 | |
IsEvenInteger |
如果值是偶數整數,則傳回 true。 2.0 會傳 true 回 ,而 2.2 會傳 false 回 。 |
|
IsFinite |
如果值不是無限且不是 NaN ,則傳回 true。 |
|
IsImaginaryNumber |
如果值為零實數部分,則傳回 true。 這表示 0 是虛構的, 1 + 1i 不是。 |
|
IsInfinity |
如果值代表無限大,則傳回 true。 | |
IsInteger |
如果值為整數,則傳回 true。 2.0 和 3.0 會傳回 true ,而 2.2 和 3.1 則傳回 false 。 |
|
IsNaN |
如果值表示 NaN ,則傳回 true。 |
|
IsNegative |
如果值為負數,則傳回 true。 這包括 -0.0。 | |
IsPositive |
如果值為正數,則傳回 true。 這包括 0 和 +0.0。 | |
IsRealNumber |
如果值為零虛數部分,則傳回 true。 這表示 0 和所有INumber<T> 類型一樣真實。 |
|
IsZero |
如果值代表零,則傳回 true。 這包括 0、+0.0 和 -0.0。 | |
MaxMagnitude |
傳回具有較大絕對值的值,如果任一輸入為 NaN ,則傳NaN 回 。 |
|
MaxMagnitudeNumber |
傳回具有較大絕對值的值,如果一個輸入為 NaN ,則會傳回數位。 |
|
MinMagnitude |
傳回具有較小絕對值的值,如果任一輸入為 NaN ,則傳NaN 回 。 |
|
MinMagnitudeNumber |
傳回具有較小絕對值的值,如果一個輸入為 NaN ,則會傳回數位。 |
|
ISignedNumber<TSelf> | NegativeOne |
取得型別的值 -1。 |
1若要協助瞭解這三 Create*
種方法的行為,請考慮下列範例。
當給定值過大時的範例:
-
byte.CreateChecked(384)
會拋出 OverflowException。 -
byte.CreateSaturating(384)
會傳回 255,因為 384 大於 Byte.MaxValue (也就是 255)。 -
byte.CreateTruncating(384)
會傳回 128,因為它會採用最低的 8 位(384 的十六進位表示,0x0180
而最低的 8 位為0x80
,也就是 128 位)。
當指定數值過小的範例:
-
byte.CreateChecked(-384)
會拋出 OverflowException。 -
byte.CreateSaturating(-384)
會傳回 0,因為 -384 小於 Byte.MinValue (也就是 0)。 -
byte.CreateTruncating(-384)
會傳回 128,因為它會採用最低的 8 位(384 的十六進位表示,0xFE80
而最低的 8 位為0x80
,也就是 128 位)。
方法 Create*
也有 IEEE 754 浮點類型的特殊考慮,例如 float
和 double
,因為它們具有特殊值 PositiveInfinity
、 NegativeInfinity
和 NaN
。 這三個 Create*
API 的行為如同 CreateSaturating
。 此外,雖然 MinValue
和 MaxValue
代表最大的負/正數「標準」數位,但實際的最小值和最大值為 NegativeInfinity
和 PositiveInfinity
,因此會改為固定這些值。
操作用戶介面
運算子介面對應於 C# 語言可用的各種運算子。
- 它們不會明確配對運算,例如乘法和除法,因為這對所有類型都不正確。 例如,
Matrix4x4 * Matrix4x4
有效,但Matrix4x4 / Matrix4x4
無效。 - 它們通常允許輸入和結果類型不同,以支援案例,例如分割兩個
double
整數來取得 ,例如 ,3 / 2 = 1.5
或計算一組整數的平均值。
介面名稱 | 已定義的運算子 |
---|---|
IAdditionOperators<TSelf,TOther,TResult> | x + y |
IBitwiseOperators<TSelf,TOther,TResult> |
x & y 、 'x |y'、、 x ^ y 和 ~x |
IComparisonOperators<TSelf,TOther,TResult> |
x < y 、x > y 、x <= y 和 x >= y |
IDecrementOperators<TSelf> |
--x 和 x-- |
IDivisionOperators<TSelf,TOther,TResult> | x / y |
IEqualityOperators<TSelf,TOther,TResult> |
x == y 和 x != y |
IIncrementOperators<TSelf> |
++x 和 x++ |
IModulusOperators<TSelf,TOther,TResult> | x % y |
IMultiplyOperators<TSelf,TOther,TResult> | x * y |
IShiftOperators<TSelf,TOther,TResult> |
x << y 和 x >> y |
ISubtractionOperators<TSelf,TOther,TResult> | x - y |
IUnaryNegationOperators<TSelf,TResult> | -x |
IUnaryPlusOperators<TSelf,TResult> | +x |
備註
有些介面除了定義一般未核取的運算子之外,還定義了已核取的運算子。 已檢查的運算子會在已檢查的上下文中呼叫,並允許使用者定義型別來定義溢位行為。 例如,如果您實作一個已檢查的運算子,如 CheckedSubtraction(TSelf, TOther),則您也必須實作一個未檢查的運算子,如 Subtraction(TSelf, TOther)。
函式介面
函式介面會定義比特定 數值介面更廣泛地套用的常見數學 API。 這些介面都是由 IFloatingPointIeee754<TSelf>實作,而且未來可能會由其他相關類型實作。
介面名稱 | 說明 |
---|---|
IExponentialFunctions<TSelf> | 公開支援 e^x 、e^x - 1 、2^x 、2^x - 1 、10^x 和 10^x - 1 的指數函式。 |
IHyperbolicFunctions<TSelf> | 公開支援 acosh(x) 、asinh(x) 、atanh(x) 、cosh(x) 、sinh(x) 和 tanh(x) 的雙曲函數。 |
ILogarithmicFunctions<TSelf> | 公開支援 ln(x) 、、 ln(x + 1) 、 log2(x) 、 log2(x + 1) 、 log10(x) 和 的 log10(x + 1) 對數函式。 |
IPowerFunctions<TSelf> | 公開支援的電源函式 x^y 。 |
IRootFunctions<TSelf> | 公開支援cbrt(x) 和sqrt(x) 的根函數。 |
ITrigonometricFunctions<TSelf> | 公開支援 acos(x) 、、 asin(x) 、 atan(x) 、 cos(x) 、 sin(x) 和 tan(x) 的三角函數。 |
解析和格式化介面
剖析和格式化是程序設計的核心概念。 將使用者輸入轉換成指定類型或向用戶顯示類型時,通常會使用它們。 這些介面位於 命名空間中 System 。
介面名稱 | 說明 |
---|---|
IParsable<TSelf> | 提供T.Parse(string, IFormatProvider) 和T.TryParse(string, IFormatProvider, out TSelf) 的支援。 |
ISpanParsable<TSelf> | 提供T.Parse(ReadOnlySpan<char>, IFormatProvider) 和T.TryParse(ReadOnlySpan<char>, IFormatProvider, out TSelf) 的支援。 |
IFormattable 1 | 公開的支援 value.ToString(string, IFormatProvider) 。 |
ISpanFormattable 1 | 公開的支援 value.TryFormat(Span<char>, out int, ReadOnlySpan<char>, IFormatProvider) 。 |
1這個介面不是新的,也不是泛型的。 不過,它由所有數字類型實作,並代表IParsable
的反向運算。
例如,下列程式會採用兩個數位做為輸入,並使用泛型方法從主控台讀取數位,其中類型參數限制為 IParsable<TSelf>。 它會使用泛型方法計算平均值,其中輸入和結果值的型別參數限制為 INumber<TSelf>,然後將結果顯示給控制台。
using System.Globalization;
using System.Numerics;
static TResult Average<T, TResult>(T first, T second)
where T : INumber<T>
where TResult : INumber<TResult>
{
return TResult.CreateChecked( (first + second) / T.CreateChecked(2) );
}
static T ParseInvariant<T>(string s)
where T : IParsable<T>
{
return T.Parse(s, CultureInfo.InvariantCulture);
}
Console.Write("First number: ");
var left = ParseInvariant<float>(Console.ReadLine());
Console.Write("Second number: ");
var right = ParseInvariant<float>(Console.ReadLine());
Console.WriteLine($"Result: {Average<float, float>(left, right)}");
/* This code displays output similar to:
First number: 5.0
Second number: 6
Result: 5.5
*/