Allmän matematik

.NET 7 introducerar nya matematiska allmänna gränssnitt i basklassbiblioteket. Tillgängligheten för dessa gränssnitt innebär att du kan begränsa en typparameter av en allmän typ eller metod till "sifferliknande". Dessutom kan du med C# 11 och senare definiera static virtual gränssnittsmedlemmar. Eftersom operatorer måste deklareras som staticlåter den här nya C#-funktionen operatorer deklareras i de nya gränssnitten för nummerliknande typer.

Tillsammans gör de här innovationerna att du kan utföra matematiska operationer generiskt, det vill: utan att behöva veta exakt vilken typ du arbetar med. Om du till exempel vill skriva en metod som lägger till två tal, var du tidigare tvungen att lägga till en överlagring av metoden för varje typ (till exempel static int Add(int first, int second) och static float Add(float first, float second)). Nu kan du skriva en enda allmän metod, där typparametern är begränsad till en talliknande typ. Till exempel:

static T Add<T>(T left, T right)
    where T : INumber<T>
{
    return left + right;
}

I den här metoden är typparametern T begränsad till en typ som implementerar det nya INumber<TSelf> gränssnittet. INumber<TSelf>implementerar IAdditionOperators<TSelf,TOther,TResult> gränssnittet, som innehåller operatorn + . Det gör att metoden allmänt kan lägga till de två talen. Metoden kan användas med någon av . NET:s inbyggda numeriska typer eftersom de alla har uppdaterats för implementering INumber<TSelf> i .NET 7.

Biblioteksförfattare drar mest nytta av de allmänna matematiska gränssnitten eftersom de kan förenkla sin kodbas genom att ta bort "redundanta" överlagringar. Andra utvecklare kommer att gynnas indirekt, eftersom DE API:er som de använder kan börja stödja fler typer.

Gränssnitten

Gränssnitten har utformats för att vara tillräckligt detaljerade så att användarna kan definiera sina egna gränssnitt ovanpå, samtidigt som de är tillräckligt detaljerade för att de ska vara lätta att använda. I den utsträckningen finns det några centrala numeriska gränssnitt som de flesta användare interagerar med, till exempel INumber<TSelf> och IBinaryInteger<TSelf>. De mer detaljerade gränssnitten, till exempel IAdditionOperators<TSelf,TOther,TResult> och ITrigonometricFunctions<TSelf>, stöder dessa typer och är tillgängliga för utvecklare som definierar sina egna domänspecifika numeriska gränssnitt.

Numeriska gränssnitt

I det här avsnittet beskrivs gränssnitten i System.Numerics som beskriver sifferliknande typer och vilka funktioner som är tillgängliga för dem.

Gränssnittsnamn beskrivning
IBinaryFloatingPointIeee754<TSelf> Exponerar API:er som är gemensamma för binära flyttalstyper1 som implementerar IEEE 754-standarden.
IBinaryInteger<TSelf> Exponerar API:er som är gemensamma för binära heltal2.
IBinaryNumber<TSelf> Exponerar API:er som är gemensamma för binära tal.
IFloatingPoint<TSelf> Exponerar API:er som är gemensamma för flyttalstyper.
IFloatingPointIeee754<TSelf> Exponerar API:er som är gemensamma för flyttalstyper som implementerar IEEE 754-standarden.
INumber<TSelf> Exponerar API:er som är gemensamma för jämförbara taltyper (i själva verket den "riktiga" taldomänen).
INumberBase<TSelf> Exponerar API:er som är gemensamma för alla nummertyper (i praktiken den "komplexa" taldomänen).
ISignedNumber<TSelf> Exponerar API:er som är gemensamma för alla signerade nummertyper (till exempel begreppet NegativeOne).
IUnsignedNumber<TSelf> Exponerar API:er som är gemensamma för alla osignerade nummertyper.
IAdditiveIdentity<TSelf,TResult> Exponerar begreppet (x + T.AdditiveIdentity) == x.
IMinMaxValue<TSelf> Exponerar begreppet T.MinValue och T.MaxValue.
IMultiplicativeIdentity<TSelf,TResult> Exponerar begreppet (x * T.MultiplicativeIdentity) == x.

1De binära flyttalstyperna är Double (double), Half, och Single (float).

2De binära heltalstyperna är Byte (byte), Int16 (short), (), Int32 (int), Int64 (long), Int128, IntPtr (nint), SByte (sbyte), UInt16 (), UInt32 (ushortuint), UInt64 (ulong), UInt128och UIntPtr (nuint).

Det gränssnitt som du troligen använder direkt är INumber<TSelf>, vilket ungefär motsvarar ett verkligt tal. Om en typ implementerar det här gränssnittet innebär det att ett värde har ett tecken (detta inkluderar unsigned typer som anses vara positiva) och kan jämföras med andra värden av samma typ. INumberBase<TSelf> ger mer avancerade begrepp, till exempel komplexa och imaginära tal, till exempel kvadratroten för ett negativt tal. Andra gränssnitt, till exempel IFloatingPointIeee754<TSelf>, skapades eftersom inte alla åtgärder är meningsfulla för alla taltyper, till exempel är det bara meningsfullt att beräkna golvet i ett tal för flyttalstyper. I .NET-basklassbiblioteket implementerar IFloatingPointIeee754<TSelf> flyttalstypen Double men Int32 gör det inte.

Flera av gränssnitten implementeras också av olika typer, inklusive Char, DateOnly, DateTime, DateTimeOffset, Decimal, Guid, TimeOnlyoch TimeSpan.

I följande tabell visas några av de kärn-API:er som exponeras av varje gränssnitt.

Gränssnitt API-namn beskrivning
IBinaryInteger<TSelf> DivRem Beräknar kvoten och resten samtidigt.
LeadingZeroCount Räknar antalet inledande noll bitar i den binära representationen.
PopCount Räknar antalet angivna bitar i den binära representationen.
RotateLeft Roterar bitar åt vänster, kallas ibland även för ett cirkulärt vänsterskift.
RotateRight Roterar bitar åt höger, ibland även kallat cirkelrätt skift.
TrailingZeroCount Räknar antalet avslutande noll bitar i den binära representationen.
IFloatingPoint<TSelf> Ceiling Avrundar värdet mot positiv oändlighet. +4,5 blir +5 och -4,5 blir -4.
Floor Avrundar värdet mot negativ oändlighet. +4,5 blir +4 och -4,5 blir -5.
Round Avrundar värdet med det angivna avrundningsläget.
Truncate Avrundar värdet mot noll. +4,5 blir +4 och -4,5 blir -4.
IFloatingPointIeee754<TSelf> E Hämtar ett värde som representerar Eulers tal för typen.
Epsilon Hämtar det minsta representerande värdet som är större än noll för typen.
NaN Hämtar ett värde som representerar NaN för typen.
NegativeInfinity Hämtar ett värde som representerar -Infinity för typen.
NegativeZero Hämtar ett värde som representerar -Zero för typen.
Pi Hämtar ett värde som representerar Pi för typen.
PositiveInfinity Hämtar ett värde som representerar +Infinity för typen.
Tau Hämtar ett värde som representerar Tau (2 * Pi) för typen.
(Övrigt) (Implementerar den fullständiga uppsättningen gränssnitt som anges under Funktionsgränssnitt.)
INumber<TSelf> Clamp Begränsar ett värde till högst och inte mindre än det angivna minsta och högsta värdet.
CopySign Anger tecknet för ett angivet värde till samma som ett annat angivet värde.
Max Returnerar större av två värden och returnerar NaN om någon av indata är NaN.
MaxNumber Returnerar det större av två värden och returnerar talet om en indata är NaN.
Min Returnerar mindre av två värden och returnerar NaN om någon av indata är NaN.
MinNumber Returnerar mindre av två värden och returnerar talet om en indata är NaN.
Sign Returnerar -1 för negativa värden, 0 för noll och +1 för positiva värden.
INumberBase<TSelf> One Hämtar värdet 1 för typen.
Radix Hämtar radixet, eller basen, för typen. Int32 returnerar 2. Decimal returnerar 10.
Zero Hämtar värdet 0 för typen.
CreateChecked Skapar ett värde och genererar ett OverflowException om indata inte får plats.1
CreateSaturating Skapar ett värde, klämmer fast T.MinValue eller T.MaxValue om indata inte får plats.1
CreateTruncating Skapar ett värde från ett annat värde och omsluter om indata inte får plats.1
IsComplexNumber Returnerar true om värdet har en icke-noll verklig del och en icke-noll imaginär del.
IsEvenInteger Returnerar sant om värdet är ett jämnt heltal. 2,0 returnerar true, och 2,2 returnerar false.
IsFinite Returnerar sant om värdet inte är oändligt och inte NaN.
IsImaginaryNumber Returnerar sant om värdet har en verklig nolldel. Det betyder att 0 det är imaginärt och 1 + 1i inte.
IsInfinity Returnerar sant om värdet representerar oändligheten.
IsInteger Returnerar sant om värdet är ett heltal. 2.0 och 3.0 returnerar true, och 2.2 och 3.1 returnerar false.
IsNaN Returnerar true om värdet representerar NaN.
IsNegative Returnerar sant om värdet är negativt. Detta inkluderar -0.0.
IsPositive Returnerar sant om värdet är positivt. Detta inkluderar 0 och +0.0.
IsRealNumber Returnerar sant om värdet har en noll imaginär del. Det innebär att 0 är verkligt, liksom alla INumber<T> typer.
IsZero Returnerar sant om värdet representerar noll. Detta inkluderar 0, +0.0 och -0.0.
MaxMagnitude Returnerar värdet med ett större absolut värde och returnerar NaN om någon av indata är NaN.
MaxMagnitudeNumber Returnerar värdet med ett större absolut värde och returnerar talet om en indata är NaN.
MinMagnitude Returnerar värdet med ett mindre absolut värde och returnerar NaN om någon av indata är NaN.
MinMagnitudeNumber Returnerar värdet med ett mindre absolut värde och returnerar talet om en indata är NaN.
ISignedNumber<TSelf> NegativeOne Hämtar värdet -1 för typen.

1Tänk på följande exempel för att förstå beteendet för de tre Create* metoderna.

Exempel när du får ett värde som är för stort:

  • byte.CreateChecked(384) genererar en OverflowException.
  • byte.CreateSaturating(384) returnerar 255 eftersom 384 är större än Byte.MaxValue (vilket är 255).
  • byte.CreateTruncating(384) returnerar 128 eftersom den tar de lägsta 8 bitarna (384 har en hexrepresentation av 0x0180, och de lägsta 8 bitarna är 0x80, vilket är 128).

Exempel när du får ett värde som är för litet:

  • byte.CreateChecked(-384) genererar en OverflowException.
  • byte.CreateSaturating(-384) returnerar 0 eftersom -384 är mindre än Byte.MinValue (vilket är 0).
  • byte.CreateTruncating(-384) returnerar 128 eftersom den tar de lägsta 8 bitarna (384 har en hexrepresentation av 0xFE80, och de lägsta 8 bitarna är 0x80, vilket är 128).

Metoderna Create* har också några särskilda överväganden för IEEE 754-flyttalstyper som float och double, eftersom de har specialvärdena PositiveInfinity, NegativeInfinityoch NaN. Alla tre Create* API:erna fungerar som CreateSaturating. Även om MinValue och MaxValue representerar det största negativa/positiva "normala" talet, är NegativeInfinity de faktiska lägsta och högsta värdena och PositiveInfinity, så att de klämmer fast dessa värden i stället.

Operatorgränssnitt

Operatorgränssnitten motsvarar de olika operatorer som är tillgängliga för C#-språket.

  • De parkopplar uttryckligen inte åtgärder som multiplikation och division eftersom det inte är korrekt för alla typer. Är till exempel Matrix4x4 * Matrix4x4 giltigt, men Matrix4x4 / Matrix4x4 är inte giltigt.
  • De tillåter vanligtvis att indata- och resultattyperna skiljer sig åt för att stödja scenarier, till exempel att dela upp två heltal för att hämta ett double, till exempel 3 / 2 = 1.5, eller beräkna medelvärdet för en uppsättning heltal.
Gränssnittsnamn Definierade operatorer
IAdditionOperators<TSelf,TOther,TResult> x + y
IBitwiseOperators<TSelf,TOther,TResult> x & y, 'x | y', x ^ y, och ~x
IComparisonOperators<TSelf,TOther,TResult> x < y, x > y, x <= yoch x >= y
IDecrementOperators<TSelf> --x och x--
IDivisionOperators<TSelf,TOther,TResult> x / y
IEqualityOperators<TSelf,TOther,TResult> x == y och x != y
IIncrementOperators<TSelf> ++x och x++
IModulusOperators<TSelf,TOther,TResult> x % y
IMultiplyOperators<TSelf,TOther,TResult> x * y
IShiftOperators<TSelf,TOther,TResult> x << y och x >> y
ISubtractionOperators<TSelf,TOther,TResult> x - y
IUnaryNegationOperators<TSelf,TResult> -x
IUnaryPlusOperators<TSelf,TResult> +x

Kommentar

Vissa av gränssnitten definierar en kontrollerad operator utöver en vanlig okontrollerad operator. Kontrollerade operatorer anropas i kontrollerade kontexter och tillåter att en användardefinierad typ definierar överflödesbeteende. Om du implementerar en kontrollerad operator, till exempel , CheckedSubtraction(TSelf, TOther)måste du även implementera den avmarkerade operatorn, Subtraction(TSelf, TOther)till exempel .

Funktionsgränssnitt

Funktionsgränssnitten definierar vanliga matematiska API:er som tillämpas bredare än på ett specifikt numeriskt gränssnitt. Alla dessa gränssnitt implementeras av IFloatingPointIeee754<TSelf>och kan implementeras av andra relevanta typer i framtiden.

Gränssnittsnamn beskrivning
IExponentialFunctions<TSelf> Exponerar exponentiella funktioner som stöder e^x, e^x - 1, 2^x, 2^x - 1, 10^xoch 10^x - 1.
IHyperbolicFunctions<TSelf> Exponerar hyperboliska funktioner som stöder acosh(x), asinh(x), atanh(x), cosh(x), sinh(x)och tanh(x).
ILogarithmicFunctions<TSelf> Exponerar logaritmiska funktioner som stöder ln(x), ln(x + 1), log2(x), log2(x + 1), log10(x)och log10(x + 1).
IPowerFunctions<TSelf> Exponerar energifunktioner som stöder x^y.
IRootFunctions<TSelf> Exponerar stöd för rotfunktioner cbrt(x) och sqrt(x).
ITrigonometricFunctions<TSelf> Exponerar trigonometriska funktioner som stöder acos(x), asin(x), atan(x), cos(x), sin(x)och tan(x).

Parsnings- och formateringsgränssnitt

Parsning och formatering är grundläggande begrepp inom programmering. De används ofta när användaren konverterar indata till en viss typ eller visar en typ för användaren. Dessa gränssnitt finns i System namnområdet.

Gränssnittsnamn beskrivning
IParsable<TSelf> Exponerar stöd för T.Parse(string, IFormatProvider) och T.TryParse(string, IFormatProvider, out TSelf).
ISpanParsable<TSelf> Exponerar stöd för T.Parse(ReadOnlySpan<char>, IFormatProvider) och T.TryParse(ReadOnlySpan<char>, IFormatProvider, out TSelf).
IFormattable1 Exponerar stöd för value.ToString(string, IFormatProvider).
ISpanFormattable1 Exponerar stöd för value.TryFormat(Span<char>, out int, ReadOnlySpan<char>, IFormatProvider).

1Det här gränssnittet är inte nytt och är inte heller allmänt. Den implementeras dock av alla nummertyper och representerar inverteringsåtgärden för IParsable.

Följande program tar till exempel två tal som indata och läser dem från konsolen med en allmän metod där typparametern är begränsad till IParsable<TSelf>. Det beräknar medelvärdet med en allmän metod där typparametrarna för indata- och resultatvärdena är begränsade till , INumber<TSelf>och visar sedan resultatet för konsolen.

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
*/

Se även