Udostępnij za pośrednictwem


Ogólna matematyka

Platforma .NET 7 wprowadza nowe interfejsy ogólne związane z matematyką do biblioteki klas bazowych. Dostępność tych interfejsów oznacza, że można ograniczyć parametr typu typu lub metody, aby był "podobny do liczby". Ponadto język C# 11 i nowsze umożliwiają definiowanie static virtual elementów członkowskich interfejsu. Ponieważ operatory muszą być zadeklarowane jako static, ta nowa funkcja języka C# umożliwia zadeklarowanie operatorów w nowych interfejsach dla typów przypominających liczbę.

Razem te innowacje umożliwiają ogólne wykonywanie operacji matematycznych — czyli bez konieczności znajomości dokładnego typu, z którym pracujesz. Jeśli na przykład chcesz napisać metodę, która dodaje dwie liczby, wcześniej trzeba było dodać przeciążenie metody dla każdego typu (na przykład static int Add(int first, int second) i static float Add(float first, float second)). Teraz możesz napisać pojedynczą, ogólną metodę, w której parametr typu jest ograniczony do typu przypominającego liczbę. Na przykład:

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

W tej metodzie parametr T typu jest ograniczony do typu, który implementuje nowy INumber<TSelf> interfejs. INumber<TSelf> implementuje IAdditionOperators<TSelf,TOther,TResult> interfejs, który zawiera operator +. Dzięki tej metodzie można w sposób ogólny dodać dwie liczby. Metodę można użyć z dowolnym elementem . Wbudowane typy liczbowe platformy NET, ponieważ wszystkie zostały zaktualizowane do implementacji INumber<TSelf> na platformie .NET 7.

Autorzy bibliotek będą korzystać z większości ogólnych interfejsów matematycznych, ponieważ mogą uprościć bazę kodu, usuwając przeciążenia "nadmiarowe". Inni deweloperzy będą korzystać pośrednio, ponieważ używane przez nich interfejsy API mogą zacząć obsługiwać więcej typów.

Interfejsy

Interfejsy zostały zaprojektowane tak, aby były wystarczająco szczegółowe, aby użytkownicy mogli definiować własne interfejsy na górze, a jednocześnie są wystarczająco szczegółowe, że są łatwe do spożycia. W tym zakresie istnieje kilka podstawowych interfejsów liczbowych, z którymi większość użytkowników będzie korzystać, takich jak INumber<TSelf> i IBinaryInteger<TSelf>. Bardziej szczegółowe interfejsy, takie jak IAdditionOperators<TSelf,TOther,TResult> i ITrigonometricFunctions<TSelf>, obsługują te typy i są dostępne dla deweloperów, którzy definiują własne interfejsy liczbowe specyficzne dla domeny.

Interfejsy liczbowe

W tej sekcji opisano interfejsy opisane w System.Numerics nich typy podobne do liczb i dostępne dla nich funkcje.

Nazwa interfejsu opis
IBinaryFloatingPointIeee754<TSelf> Uwidacznia interfejsy API wspólne dla binarnych typówzmiennoprzecinkowych 1, które implementują standard IEEE 754.
IBinaryInteger<TSelf> Uwidacznia interfejsy API wspólne dla liczbbinarnych 2.
IBinaryNumber<TSelf> Uwidacznia interfejsy API wspólne dla liczb binarnych.
IFloatingPoint<TSelf> Uwidacznia interfejsy API wspólne dla typów zmiennoprzecinkowych.
IFloatingPointIeee754<TSelf> Uwidacznia interfejsy API wspólne dla typów zmiennoprzecinkowych, które implementują standard IEEE 754.
INumber<TSelf> Uwidacznia interfejsy API wspólne dla porównywalnych typów liczb (w rzeczywistości "domena liczb rzeczywistych").
INumberBase<TSelf> Uwidacznia interfejsy API wspólne dla wszystkich typów liczb (w rzeczywistości "złożona" domena liczb).
ISignedNumber<TSelf> Uwidacznia interfejsy API wspólne dla wszystkich typów cyfr podpisanych (takich jak koncepcja ).NegativeOne
IUnsignedNumber<TSelf> Uwidacznia interfejsy API wspólne dla wszystkich niepodpisanych typów liczb.
IAdditiveIdentity<TSelf,TResult> Uwidacznia koncepcję .(x + T.AdditiveIdentity) == x
IMinMaxValue<TSelf> Uwidacznia koncepcję elementów T.MinValue i T.MaxValue.
IMultiplicativeIdentity<TSelf,TResult> Uwidacznia koncepcję .(x * T.MultiplicativeIdentity) == x

1Binarne typy zmiennoprzecinkowe to Double (double), Halfi Single (float).

2Typy liczb całkowitych binarnych to Byte (byte), Int16 (short), Int32 (int), (), (long), Int64 (), IntPtrInt128(sbyteUInt16nintSByte), (), (uintUInt32ushort), UInt64ulong() UInt128i UIntPtr (nuint).

Interfejs, którego najprawdopodobniej używasz bezpośrednio, to INumber<TSelf>, który w przybliżeniu odpowiada liczbie rzeczywistej. Jeśli typ implementuje ten interfejs, oznacza to, że wartość ma znak (obejmuje unsigned to typy, które są uważane za dodatnie) i mogą być porównywane z innymi wartościami tego samego typu. INumberBase<TSelf> nadaje bardziej zaawansowane koncepcje, takie jak liczby złożone i wyimaginowane , na przykład pierwiastek kwadratowy liczby ujemnej. Inne interfejsy, takie jak IFloatingPointIeee754<TSelf>, zostały utworzone, ponieważ nie wszystkie operacje mają sens dla wszystkich typów liczb — na przykład obliczanie podłogi liczby ma sens tylko dla typów zmiennoprzecinkowych. W bibliotece klas bazowych platformy .NET typ Double zmiennoprzecinkowa implementuje IFloatingPointIeee754<TSelf> , ale Int32 nie.

Kilka interfejsów jest również implementowanych przez różne inne typy, w tym Char, , DateTimeDecimalDateTimeOffsetGuidDateOnly, TimeOnlyi .TimeSpan

W poniższej tabeli przedstawiono niektóre podstawowe interfejsy API uwidocznione przez każdy interfejs.

Interfejs Nazwa interfejsu API opis
IBinaryInteger<TSelf> DivRem Oblicza iloraz i pozostałą część.
LeadingZeroCount Zlicza liczbę bitów zer wiodących w reprezentacji binarnej.
PopCount Zlicza bity ustawione w reprezentacji binarnej.
RotateLeft Obraca bity w lewo, czasami nazywane również okrągłym przesunięciem w lewo.
RotateRight Obraca bity w prawo, czasami nazywane również okrągłym przesunięciem w prawo.
TrailingZeroCount Zlicza liczbę bitów końcowych zero w reprezentacji binarnej.
IFloatingPoint<TSelf> Ceiling Zaokrągla wartość w kierunku nieskończoności dodatniej. +4.5 staje się +5, a wartość -4.5 staje się -4.
Floor Zaokrągla wartość w kierunku nieskończoności ujemnej. +4.5 staje się +4, a wartość -4.5 staje się -5.
Round Zaokrągla wartość przy użyciu określonego trybu zaokrąglania.
Truncate Zaokrągla wartość do zera. +4.5 staje się +4, a wartość -4.5 staje się -4.
IFloatingPointIeee754<TSelf> E Pobiera wartość reprezentującą liczbę Eulera dla typu.
Epsilon Pobiera najmniejszą reprezentującą wartość większą niż zero dla typu.
NaN Pobiera wartość reprezentującą NaN typ.
NegativeInfinity Pobiera wartość reprezentującą -Infinity typ.
NegativeZero Pobiera wartość reprezentującą -Zero typ.
Pi Pobiera wartość reprezentującą Pi typ.
PositiveInfinity Pobiera wartość reprezentującą +Infinity typ.
Tau Pobiera wartość reprezentującą Tau (2 * Pi) dla typu.
(Inne) (Implementuje pełny zestaw interfejsów wymienionych w obszarze Interfejsy funkcji).
INumber<TSelf> Clamp Ogranicza wartość do nie więcej i nie mniej niż określona wartość minimalna i maksymalna.
CopySign Ustawia znak określonej wartości na taki sam jak inna określona wartość.
Max Zwraca większą z dwóch wartości, zwracając NaN wartość , jeśli jeden z danych wejściowych to NaN.
MaxNumber Zwraca większą z dwóch wartości, zwracając liczbę, jeśli jedno dane wejściowe to NaN.
Min Zwraca mniejszą z dwóch wartości, zwracając NaN wartość , jeśli jeden z danych wejściowych to NaN.
MinNumber Zwraca mniejszą z dwóch wartości, zwracając liczbę, jeśli jedno dane wejściowe to NaN.
Sign Zwraca wartość -1 dla wartości ujemnych, 0 dla zera i +1 dla wartości dodatnich.
INumberBase<TSelf> One Pobiera wartość 1 dla typu.
Radix Pobiera promienie lub podstawę dla typu. Int32 zwraca wartość 2. Decimal zwraca 10.
Zero Pobiera wartość 0 dla typu.
CreateChecked Tworzy wartość, zgłaszając OverflowException wartość, jeśli dane wejściowe nie mogą pasować.1
CreateSaturating Tworzy wartość, zaciskanie do T.MinValue lub T.MaxValue jeśli dane wejściowe nie mogą się zmieścić.1
CreateTruncating Tworzy wartość z innej wartości, opakowujące się, jeśli dane wejściowe nie mogą pasować.1
IsComplexNumber Zwraca wartość true, jeśli wartość ma niezerową część rzeczywistą i niezerową część wyimaginowaną.
IsEvenInteger Zwraca wartość true, jeśli wartość jest parzystą liczbą całkowitą. Funkcja 2.0 zwraca wartość true, a wartość 2.2 zwraca wartość false.
IsFinite Zwraca wartość true, jeśli wartość nie jest nieskończona, a nie NaN.
IsImaginaryNumber Zwraca wartość true, jeśli wartość ma wartość zero rzeczywistej. Oznacza 0 to, że jest wyimaginowany i 1 + 1i nie jest.
IsInfinity Zwraca wartość true, jeśli wartość reprezentuje nieskończoność.
IsInteger Zwraca wartość true, jeśli wartość jest liczbą całkowitą. 2.0 i 3.0 zwracają wartości true, a 2.2 i 3.1 zwracają wartość false.
IsNaN Zwraca wartość true, jeśli wartość reprezentuje NaNwartość .
IsNegative Zwraca wartość true, jeśli wartość jest ujemna. Obejmuje to wartość -0.0.
IsPositive Zwraca wartość true, jeśli wartość jest dodatnia. Obejmuje to 0 i +0.0.
IsRealNumber Zwraca wartość true, jeśli wartość ma zerową część wyimaginowaną. Oznacza to, że wartość 0 jest prawdziwa, podobnie jak wszystkie INumber<T> typy.
IsZero Zwraca wartość true, jeśli wartość reprezentuje zero. Obejmuje to wartości 0, +0.0 i -0.0.
MaxMagnitude Zwraca wartość z większą wartością bezwzględną, zwracając NaN wartość , jeśli dane wejściowe to NaN.
MaxMagnitudeNumber Zwraca wartość z większą wartością bezwzględną, zwracając liczbę, jeśli jedno dane wejściowe to NaN.
MinMagnitude Zwraca wartość z mniejszą wartością bezwzględną, zwracając NaN wartość , jeśli dane wejściowe to NaN.
MinMagnitudeNumber Zwraca wartość z mniejszą wartością bezwzględną, zwracając liczbę, jeśli jedno dane wejściowe to NaN.
ISignedNumber<TSelf> NegativeOne Pobiera wartość -1 dla typu.

1Aby ułatwić zrozumienie zachowania trzech Create* metod, rozważ następujące przykłady.

Przykład, gdy dana wartość jest zbyt duża:

  • byte.CreateChecked(384) element zgłosi element OverflowException.
  • byte.CreateSaturating(384) Zwraca wartość 255, ponieważ wartość 384 jest większa niż Byte.MaxValue (czyli 255).
  • byte.CreateTruncating(384) Zwraca wartość 128, ponieważ przyjmuje najniższe 8 bitów (384 ma reprezentację 0x0180szesnastkową , a najniższa 8 bitów to 0x80, czyli 128).

Przykład, gdy dana wartość jest za mała:

  • byte.CreateChecked(-384) element zgłosi element OverflowException.
  • byte.CreateSaturating(-384) Zwraca wartość 0, ponieważ wartość -384 jest mniejsza niż Byte.MinValue (czyli 0).
  • byte.CreateTruncating(-384) Zwraca wartość 128, ponieważ przyjmuje najniższe 8 bitów (384 ma reprezentację 0xFE80szesnastkową , a najniższa 8 bitów to 0x80, czyli 128).

Metody Create* mają również pewne specjalne zagadnienia dotyczące typów zmiennoprzecinkowych IEEE 754, takich jak float i double, ponieważ mają specjalne wartości PositiveInfinity, NegativeInfinityi NaN. Wszystkie trzy Create* interfejsy API zachowują się jako CreateSaturating. Ponadto, podczas gdy MinValue i MaxValue reprezentują największą ujemną/dodatnią liczbę "normalną", rzeczywiste wartości minimalne i maksymalne są NegativeInfinity i PositiveInfinity, więc zamiast tego zaciskają się do tych wartości.

Interfejsy operatorów

Interfejsy operatora odpowiadają różnym operatorom dostępnym dla języka C#.

  • Jawnie nie łączą one operacji, takich jak mnożenie i dzielenie, ponieważ nie jest to poprawne dla wszystkich typów. Na przykład Matrix4x4 * Matrix4x4 jest prawidłowy, ale Matrix4x4 / Matrix4x4 nie jest prawidłowy.
  • Zazwyczaj umożliwiają one różnice między typami danych wejściowych i wynikowych w celu obsługi scenariuszy, takich jak dzielenie dwóch liczb całkowitych w celu uzyskania doublewartości , na przykład 3 / 2 = 1.5, lub obliczanie średniej zestawu liczb całkowitych.
Nazwa interfejsu Zdefiniowane operatory
IAdditionOperators<TSelf,TOther,TResult> x + y
IBitwiseOperators<TSelf,TOther,TResult> x & y, 'x | y', x ^ yi ~x
IComparisonOperators<TSelf,TOther,TResult> x < y, x > y, x <= yi x >= y
IDecrementOperators<TSelf> --x i x--
IDivisionOperators<TSelf,TOther,TResult> x / y
IEqualityOperators<TSelf,TOther,TResult> x == y i x != y
IIncrementOperators<TSelf> ++x i x++
IModulusOperators<TSelf,TOther,TResult> x % y
IMultiplyOperators<TSelf,TOther,TResult> x * y
IShiftOperators<TSelf,TOther,TResult> x << y i x >> y
ISubtractionOperators<TSelf,TOther,TResult> x - y
IUnaryNegationOperators<TSelf,TResult> -x
IUnaryPlusOperators<TSelf,TResult> +x

Uwaga

Niektóre interfejsy definiują operatora zaznaczonego oprócz zwykłego nieznaczonego operatora. Sprawdzone operatory są wywoływane w zaznaczonych kontekstach i umożliwiają definiowanie zachowania przepełnienia zdefiniowanego przez użytkownika. Jeśli na przykład zaimplementujesz sprawdzony operator , CheckedSubtraction(TSelf, TOther)musisz również zaimplementować niezaznaczonego operatora, na przykład Subtraction(TSelf, TOther).

Interfejsy funkcji

Interfejsy funkcji definiują typowe interfejsy API matematyczne, które mają zastosowanie szerzej niż do określonego interfejsu liczbowego. Wszystkie te interfejsy są implementowane przez IFloatingPointIeee754<TSelf>program i mogą zostać zaimplementowane przez inne odpowiednie typy w przyszłości.

Nazwa interfejsu opis
IExponentialFunctions<TSelf> Uwidacznia funkcje wykładnicze obsługujące e^xfunkcje , , e^x - 1, 2^x, 2^x - 110^x, i 10^x - 1.
IHyperbolicFunctions<TSelf> Uwidacznia funkcje hiperboliczne obsługujące acosh(x)funkcje , , asinh(x), atanh(x)cosh(x), sinh(x)i tanh(x).
ILogarithmicFunctions<TSelf> Uwidacznia funkcje logarytmicznych obsługujące ln(x)funkcje , , ln(x + 1), log2(x)log2(x + 1), log10(x), i log10(x + 1).
IPowerFunctions<TSelf> Uwidacznia funkcje zasilania obsługujące funkcję x^y.
IRootFunctions<TSelf> Uwidacznia funkcje główne obsługujące cbrt(x) i sqrt(x).
ITrigonometricFunctions<TSelf> Udostępnia funkcje trygonometryczne obsługujące acos(x)funkcje , , asin(x), atan(x), cos(x), sin(x)i tan(x).

Interfejsy analizowania i formatowania

Analizowanie i formatowanie to podstawowe pojęcia w programowaniu. Są one często używane podczas konwertowania danych wejściowych użytkownika na dany typ lub wyświetlania typu użytkownikowi. Te interfejsy znajdują się w System przestrzeni nazw.

Nazwa interfejsu opis
IParsable<TSelf> Uwidacznia obsługę poleceń T.Parse(string, IFormatProvider) i T.TryParse(string, IFormatProvider, out TSelf).
ISpanParsable<TSelf> Uwidacznia obsługę poleceń T.Parse(ReadOnlySpan<char>, IFormatProvider) i T.TryParse(ReadOnlySpan<char>, IFormatProvider, out TSelf).
IFormattable1 Uwidacznia obsługę programu value.ToString(string, IFormatProvider).
ISpanFormattable1 Uwidacznia obsługę programu value.TryFormat(Span<char>, out int, ReadOnlySpan<char>, IFormatProvider).

1Ten interfejs nie jest nowy ani nie jest ogólny. Jednak jest on implementowany przez wszystkie typy liczb i reprezentuje odwrotną operację IParsable.

Na przykład poniższy program przyjmuje dwie liczby jako dane wejściowe, odczytując je z konsoli przy użyciu metody ogólnej, w której parametr typu jest ograniczony do IParsable<TSelf>. Oblicza średnią przy użyciu metody ogólnej, w której parametry typu dla wartości wejściowych i wynikowych są ograniczone do INumber<TSelf>wartości , a następnie wyświetla wynik w konsoli.

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

Zobacz też