Delen via


Algemene wiskunde

.NET 7 introduceert nieuwe wiskundige algemene interfaces voor de basisklassebibliotheek. De beschikbaarheid van deze interfaces betekent dat u een typeparameter van een algemeen type of methode kunt beperken als 'getalachtig'. Daarnaast kunt u met C# 11 en hoger interfaceleden definiërenstatic virtual. Omdat operators moeten worden gedeclareerd als static, kunnen met deze nieuwe C#-functie operators worden gedeclareerd in de nieuwe interfaces voor numerieke typen.

Met deze innovaties kunt u algemene wiskundige bewerkingen uitvoeren, dat wil zeggen, zonder dat u het exacte type hoeft te kennen waarmee u werkt. Als u bijvoorbeeld een methode wilt schrijven waarmee twee getallen worden opgetellen, moest u eerder een overbelasting van de methode voor elk type toevoegen (bijvoorbeeld static int Add(int first, int second) en static float Add(float first, float second)). U kunt nu één algemene methode schrijven, waarbij de typeparameter is beperkt tot een getalachtig type. Voorbeeld:

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

In deze methode is de typeparameter T beperkt tot een type dat de nieuwe INumber<TSelf> interface implementeert. INumber<TSelf> implementeert de IAdditionOperators<TSelf,TOther,TResult> interface, die de +-operator bevat. Hierdoor kan de methode de twee getallen algemeen optellen. De methode kan worden gebruikt met elk van de ingebouwde numerieke typen van .NET, omdat ze allemaal zijn bijgewerkt om implementatie INumber<TSelf> in .NET 7 te ondersteunen.

Bibliotheekauteurs profiteren het meeste van de algemene wiskundige interfaces, omdat ze hun codebasis kunnen vereenvoudigen door 'redundante' overbelastingen te verwijderen. Andere ontwikkelaars profiteren indirect, omdat de API's die ze gebruiken, meer typen kunnen ondersteunen.

De interfaces

De interfaces zijn ontworpen om fijnmazig genoeg te zijn zodat gebruikers hun eigen interfaces kunnen definiëren, en daarbij granulair genoeg zodat ze gemakkelijk te gebruiken zijn. In die mate zijn er enkele kernen van numerieke interfaces waarmee de meeste gebruikers communiceren, zoals INumber<TSelf> en IBinaryInteger<TSelf>. De meer verfijnde interfaces, zoals IAdditionOperators<TSelf,TOther,TResult> en ITrigonometricFunctions<TSelf>, ondersteunen deze typen en zijn beschikbaar voor ontwikkelaars die hun eigen domeinspecifieke numerieke interfaces definiëren.

Numerieke interfaces

In deze sectie worden de interfaces beschreven in System.Numerics die nummerachtige typen en de beschikbare functionaliteit beschrijven.

Interfacenaam Beschrijving
IBinaryFloatingPointIeee754<TSelf> Maakt API's beschikbaar voor binaire drijvende-komma typen die de IEEE 754-standaard implementeren.
IBinaryInteger<TSelf> Maakt API's algemeen beschikbaar voor binaire gehele getallen2.
IBinaryNumber<TSelf> Maakt API's algemeen beschikbaar voor binaire getallen.
IFloatingPoint<TSelf> Maakt API's algemeen beschikbaar voor komengetaltypen.
IFloatingPointIeee754<TSelf> Stelt API's beschikbaar die gangbaar zijn voor typen drijvende komma die de IEEE 754-standaard implementeren.
INumber<TSelf> Stelt API's beschikbaar die vergelijkbaar zijn met vergelijkbare getaltypen (in feite het 'reële' numerieke domein).
INumberBase<TSelf> Maakt API's algemeen beschikbaar voor alle getaltypen (in feite het 'complexe' numerieke domein).
ISignedNumber<TSelf> Stelt API's beschikbaar die voorkomen bij alle getekende getaltypen (zoals het concept NegativeOne).
IUnsignedNumber<TSelf> Maakt API's algemeen beschikbaar voor alle niet-ondertekende getaltypen.
IAdditiveIdentity<TSelf,TResult> Toont het concept van (x + T.AdditiveIdentity) == x.
IMinMaxValue<TSelf> Onthult het concept van T.MinValue en T.MaxValue.
IMultiplicativeIdentity<TSelf,TResult> Toont het concept van (x * T.MultiplicativeIdentity) == x.

1De binaire typen drijvende komma zijn Double (double), Half en Single (float).

2De binaire gehele getalen zijn Byte (byte), Int16 (short), Int32 (int), Int64 (long), Int128, IntPtr (nint), SByte (sbyte), UInt16 (ushort), UInt32 (uint), UInt64 (ulong), UInt128, en UIntPtr (nuint).

De interface die u waarschijnlijk rechtstreeks gebruikt, is INumber<TSelf>, wat ongeveer overeenkomt met een reëel getal. Als een type deze interface implementeert, betekent dit dat een waarde een teken heeft (dit omvat unsigned typen, die als positief worden beschouwd) en kan worden vergeleken met andere waarden van hetzelfde type. INumberBase<TSelf> geeft meer geavanceerde concepten, zoals complexe en imaginaire getallen, bijvoorbeeld de vierkantswortel van een negatief getal. Andere interfaces, zoals IFloatingPointIeee754<TSelf>, zijn gemaakt omdat niet alle bewerkingen zinvol zijn voor alle getaltypen, bijvoorbeeld het berekenen van de vloer van een getal, is alleen zinvol voor typen drijvende komma. In de .NET-basisklassebibliotheek wordt het type Double drijvende komma geïmplementeerd IFloatingPointIeee754<TSelf> , maar Int32 niet.

Verschillende interfaces worden ook geïmplementeerd door verschillende andere typen, waaronder Char, , DateOnly, DateTimeDateTimeOffset, Decimal, , Guid, , en TimeOnlyTimeSpan.

In de volgende tabel ziet u enkele van de kern-API's die door elke interface worden weergegeven.

gebruikersinterface API-naam Beschrijving
IBinaryInteger<TSelf> DivRem Berekent het quotiënt en de rest tegelijk.
LeadingZeroCount Telt het aantal voorloopnul-bits in de binaire weergave.
PopCount Telt het aantal ingestelde bits in de binaire weergave.
RotateLeft Bits worden naar links geroteerd, ook wel een cirkelvormige linksverschuiving genoemd.
RotateRight Bits worden naar rechts gedraaid, soms ook wel een circulaire verschuiving naar rechts genoemd.
TrailingZeroCount Telt het aantal achtereenvolgende nulbits in de binaire weergave.
IFloatingPoint<TSelf> Ceiling Rondt de waarde af naar positieve oneindigheid. +4,5 wordt +5 en -4,5 wordt -4.
Floor Rondt de waarde af op negatieve oneindigheid. +4,5 wordt +4 en -4,5 wordt -5.
Round Rondt de waarde af met behulp van de opgegeven afrondingsmodus.
Truncate Rondt de waarde af naar nul. +4,5 wordt +4 en -4,5 wordt -4.
IFloatingPointIeee754<TSelf> E Hiermee wordt een waarde opgehaald die het getal van Euler vertegenwoordigt voor het type.
Epsilon Hiermee haalt u de kleinste vertegenwoordigbare waarde op die groter is dan nul voor het type.
NaN Hiermee haalt u een waarde NaN op voor het type.
NegativeInfinity Hiermee haalt u een waarde -Infinity op voor het type.
NegativeZero Hiermee haalt u een waarde -Zero op voor het type.
Pi Hiermee haalt u een waarde Pi op voor het type.
PositiveInfinity Hiermee haalt u een waarde +Infinity op voor het type.
Tau Hiermee haalt u een waarde op die Tau (2 * Pi) voor het type vertegenwoordigt.
(Overig) (Implementeert de volledige set van interfaces genoemd onder Functie-interfaces.)
INumber<TSelf> Clamp Hiermee beperkt u een waarde tot niet meer en niet minder dan de opgegeven minimum- en maximumwaarde.
CopySign Hiermee stelt u het teken van een opgegeven waarde in op hetzelfde als een andere opgegeven waarde.
Max Retourneert de grotere van twee waarden, NaN als een van beide invoer NaN is.
MaxNumber Retourneert de grotere van twee waarden, waarbij het getal wordt geretourneerd als één invoer NaN is.
Min Retourneert de kleinere van twee waarden en retourneert NaN als een van beide invoer NaN is.
MinNumber Retourneert de kleinste van twee waarden, waarbij het getal wordt geretourneerd als één van de invoeren NaN is.
Sign Retourneert -1 voor negatieve waarden, 0 voor nul en +1 voor positieve waarden.
INumberBase<TSelf> One Hiermee haalt u de waarde 1 voor het type op.
Radix Bepaalt de radix of basis voor een type. Int32 retourneert 2. Decimal geeft 10 terug.
Zero Hiermee haalt u de waarde 0 voor het type op.
CreateChecked Hiermee maakt u een waarde en werpt u een OverflowException als de invoer niet kan passen.1
CreateSaturating Maakt een waarde aan, klemmen naar T.MinValue of T.MaxValue als de invoer niet past.1
CreateTruncating Hiermee maakt u een waarde op basis van een andere waarde, die rondloopt als de invoer niet past. 1
IsComplexNumber Retourneert waar als de waarde een niet-nul reëel deel en een niet-nul imaginair deel heeft.
IsEvenInteger Retourneert waar als de waarde een even geheel getal is. 2.0 retourneert trueen 2,2 retourneert false.
IsFinite Retourneert waar als de waarde niet oneindig is en niet NaN.
IsImaginaryNumber Retourneert waar als de waarde een reëel deel van nul heeft. Dit betekent 0 imaginair en 1 + 1i niet.
IsInfinity Retourneert waar als de waarde oneindigheid vertegenwoordigt.
IsInteger Retourneert true als de waarde een geheel getal is. 2.0 en 3.0 retourneren true, en 2.2 en 3.1 retourneren false.
IsNaN Retourneert true als de waarde NaN vertegenwoordigt.
IsNegative Geeft true terug als de waarde negatief is. Dit omvat -0.0.
IsPositive Retourneert waar als de waarde positief is. Dit omvat 0 en +0,0.
IsRealNumber Retourneert waar als de waarde een imaginair deel van nul heeft. Dit betekent dat 0 echt is zoals alle INumber<T> typen.
IsZero Geeft waar terug als de waarde nul is. Dit omvat 0, +0.0 en -0.0.
MaxMagnitude Keert de waarde met de grootste absolute waarde terug, waarbij NaN wordt geretourneerd als een van beide invoer NaN is.
MaxMagnitudeNumber Retourneert de waarde met de grootste absolute waarde, en retourneert het getal als één invoer NaN is.
MinMagnitude Retourneert de waarde met een kleinere absolute waarde en retourneert NaN als een van beide invoeren NaN is.
MinMagnitudeNumber Retourneert de waarde met een minder absolute waarde, waarbij het getal als één invoer wordt NaNgeretourneerd.
ISignedNumber<TSelf> NegativeOne Haalt de waarde -1 op voor het type.

1Bekijk de volgende voorbeelden om inzicht te verkrijgen in het gedrag van de drie Create* methoden.

Voorbeeld wanneer u een waarde krijgt die te groot is:

  • byte.CreateChecked(384) zal een OverflowException genereren.
  • byte.CreateSaturating(384) retourneert 255 omdat 384 groter is dan Byte.MaxValue (dat is 255).
  • byte.CreateTruncating(384) retourneert 128 omdat het de laagste 8 bits neemt (384 heeft een hexadecimale representatie van 0x0180, en de laagste 8 bits is 0x80, wat 128 is).

Voorbeeld wanneer u een waarde krijgt die te klein is:

  • byte.CreateChecked(-384) zal een OverflowException genereren.
  • byte.CreateSaturating(-384) retourneert 0 omdat -384 kleiner is dan Byte.MinValue (dat is 0).
  • byte.CreateTruncating(-384) retourneert 128 omdat het de laagste 8 bits neemt (384 heeft een hexadecimale representatie van 0xFE80, en de laagste 8 bits is 0x80, wat 128 is).

De Create* methoden hebben ook enkele speciale overwegingen voor IEEE 754-drijvendekommatypen, zoals float en double, omdat ze de speciale waarden PositiveInfinityhebben , NegativeInfinityen NaN. Alle drie Create* de API's gedragen zich als CreateSaturating. Ook al vertegenwoordigen MinValue en MaxValue het grootste negatieve/positieve "normale" getal, de daadwerkelijke minimum- en maximumwaarden zijn NegativeInfinity en PositiveInfinity, ze worden daarom begrensd tot deze waarden.

Operatorinterfaces

De operatorinterfaces komen overeen met de verschillende operators die beschikbaar zijn voor de C#-taal.

  • Ze koppelen expliciet geen bewerkingen, zoals vermenigvuldigen en delen, omdat dat niet juist is voor alle typen. Is bijvoorbeeld Matrix4x4 * Matrix4x4 geldig, maar Matrix4x4 / Matrix4x4 is niet geldig.
  • Ze staan doorgaans toe dat de invoer- en resultaattypen verschillen om scenario's te ondersteunen, zoals het delen van twee gehele getallen om bijvoorbeeld een double te verkrijgen, of het berekenen van het gemiddelde van een set gehele getallen.
Interfacenaam Gedefinieerde operators
IAdditionOperators<TSelf,TOther,TResult> x + y
IBitwiseOperators<TSelf,TOther,TResult> x & y, 'x | y', x ^ yen ~x
IComparisonOperators<TSelf,TOther,TResult> x < y, x > y, x <= y en x >= y
IDecrementOperators<TSelf> --x en x--
IDivisionOperators<TSelf,TOther,TResult> x / y
IEqualityOperators<TSelf,TOther,TResult> x == y en x != y
IIncrementOperators<TSelf> ++x en x++
IModulusOperators<TSelf,TOther,TResult> x % y
IMultiplyOperators<TSelf,TOther,TResult> x * y
IShiftOperators<TSelf,TOther,TResult> x << y en x >> y
ISubtractionOperators<TSelf,TOther,TResult> x - y
IUnaryNegationOperators<TSelf,TResult> -x
IUnaryPlusOperators<TSelf,TResult> +x

Opmerking

Sommige interfaces definiëren een gecontroleerde operator naast een reguliere niet-gecontroleerde operator. Gecontroleerde operators worden aangeroepen in gecontroleerde contexten en stellen een door de gebruiker gedefinieerd type in staat om overloopgedrag te definiëren. Als u bijvoorbeeld een gecontroleerde operator implementeert, CheckedSubtraction(TSelf, TOther)moet u ook de uitgeschakelde operator implementeren, bijvoorbeeld Subtraction(TSelf, TOther).

Functie-interfaces

De functieinterfaces definiëren algemene wiskundige API's die breder van toepassing zijn dan op een specifieke numerieke interface. Deze interfaces worden allemaal geïmplementeerd door IFloatingPointIeee754<TSelf>en kunnen in de toekomst worden geïmplementeerd door andere relevante typen.

Interfacenaam Beschrijving
IExponentialFunctions<TSelf> Stelt exponentiële functies beschikbaar die ondersteuning bieden voor e^x, e^x - 1, 2^x, 2^x - 1, 10^x en 10^x - 1.
IHyperbolicFunctions<TSelf> Stelt hyperbolische functies beschikbaar die acosh(x), asinh(x), atanh(x), cosh(x), sinh(x) en tanh(x) ondersteunen.
ILogarithmicFunctions<TSelf> Biedt logaritmische functies die ondersteuning bieden voor ln(x), ln(x + 1), log2(x), log2(x + 1), log10(x) en log10(x + 1).
IPowerFunctions<TSelf> Stelt vermogensfuncties beschikbaar die x^y ondersteunen.
IRootFunctions<TSelf> Maakt hoofdfuncties beschikbaar die ondersteunen cbrt(x) en sqrt(x).
ITrigonometricFunctions<TSelf> Maakt trigonometrische functies beschikbaar die acos(x), asin(x), atan(x), cos(x), sin(x) en tan(x) ondersteunen.

Interfaces voor parseren en opmaken

Parseren en opmaken zijn kernconcepten in programmeren. Ze worden vaak gebruikt bij het converteren van gebruikersinvoer naar een bepaald type of het weergeven van een type naar de gebruiker. Deze interfaces bevinden zich in de System naamruimte.

Interfacenaam Beschrijving
IParsable<TSelf> Biedt ondersteuning voor T.Parse(string, IFormatProvider) en T.TryParse(string, IFormatProvider, out TSelf).
ISpanParsable<TSelf> Biedt ondersteuning voor T.Parse(ReadOnlySpan<char>, IFormatProvider) en T.TryParse(ReadOnlySpan<char>, IFormatProvider, out TSelf).
IFormattable 1 Biedt ondersteuning voor value.ToString(string, IFormatProvider).
ISpanFormattable 1 Biedt ondersteuning voor value.TryFormat(Span<char>, out int, ReadOnlySpan<char>, IFormatProvider).

1Deze interface is niet nieuw en is ook niet algemeen. Het wordt echter geïmplementeerd door alle getaltypen en vertegenwoordigt de inverse werking van IParsable.

Het volgende programma gebruikt bijvoorbeeld twee getallen als invoer, waarbij deze worden gelezen vanuit de console met behulp van een algemene methode waarbij de typeparameter wordt beperkt IParsable<TSelf>. Het berekent het gemiddelde met behulp van een algemene methode waarbij de typeparameters voor de invoer- en resultaatwaarden beperkt zijn en INumber<TSelf>het resultaat vervolgens weergeeft aan de console.

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

Zie ook