Bagikan melalui


Matematika generik

.NET 7 memperkenalkan antarmuka umum terkait matematika baru ke pustaka kelas dasar. Ketersediaan antarmuka ini berarti Anda dapat membatasi parameter tipe dari jenis atau metode generik agar mirip angka. Selain itu, C# 11 dan yang lebih baru memungkinkan Anda menentukan static virtual anggota antarmuka. Karena operator harus dinyatakan sebagai static, fitur C# baru ini memungkinkan operator dideklarasikan dalam antarmuka baru untuk jenis seperti angka.

Inovasi-inovasi ini memungkinkan Anda untuk melakukan operasi matematika secara umum—yaitu, tanpa harus mengetahui jenis yang sedang Anda kerjakan. Misalnya, jika Anda ingin menulis metode yang menambahkan dua angka, sebelumnya Anda harus menambahkan overload dari metode untuk setiap tipe (misalnya, static int Add(int first, int second) dan static float Add(float first, float second)). Sekarang Anda dapat menulis metode umum tunggal, di mana parameter jenis dibatasi menjadi jenis seperti angka. Contohnya:

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

Dalam metode ini, parameter T jenis dibatasi menjadi jenis yang mengimplementasikan antarmuka baru INumber<TSelf> . INumber<TSelf> IAdditionOperators<TSelf,TOther,TResult> mengimplementasikan antarmuka, yang berisi operator + . Itu memungkinkan metode untuk secara generik menambahkan dua angka. Metode ini dapat digunakan dengan salah satu jenis numerik bawaan .NET, karena semuanya sudah diperbarui untuk mengimplementasikan INumber<TSelf> di .NET 7.

Penulis pustaka akan mendapat manfaat paling besar dari antarmuka matematika generik, karena mereka dapat menyederhanakan basis kode mereka dengan menghapus kelebihan beban "redundan". Pengembang lain akan mendapat manfaat secara tidak langsung, karena API yang mereka konsumsi dapat mulai mendukung lebih banyak jenis.

Antarmuka

Antarmuka dirancang agar cukup terperinci sehingga pengguna dapat menentukan antarmuka mereka sendiri di atasnya, sekaligus cukup terperinci sehingga mudah dikonsumsi. Sejauh itu, ada beberapa antarmuka numerik inti yang akan berinteraksi dengan sebagian besar pengguna, seperti INumber<TSelf> dan IBinaryInteger<TSelf>. Antarmuka yang lebih halus, seperti IAdditionOperators<TSelf,TOther,TResult> dan ITrigonometricFunctions<TSelf>, mendukung jenis ini dan tersedia untuk pengembang yang menentukan antarmuka numerik khusus domain mereka sendiri.

Antarmuka numerik

Bagian ini menjelaskan antarmuka dalam System.Numerics yang menjelaskan jenis yang mirip dengan angka dan fungsionalitas yang dapat digunakan.

Nama antarmuka Deskripsi
IBinaryFloatingPointIeee754<TSelf> Mengekspos API umum untuk jenis floating-point biner yang mengimplementasikan standar IEEE 754.
IBinaryInteger<TSelf> Mengekspos API yang umum untuk bilangan bulat biner2.
IBinaryNumber<TSelf> Memaparkan API yang umum untuk angka biner.
IFloatingPoint<TSelf> Mengekspos API yang umum untuk tipe floating-point.
IFloatingPointIeee754<TSelf> Mengekspos API yang umum untuk jenis floating-point yang mengimplementasikan standar IEEE 754.
INumber<TSelf> Mengekspos API yang umum untuk jenis angka yang bisa dibandingkan (dalam arti "domain angka riil").
INumberBase<TSelf> Mengekspos API umum ke semua jenis angka (secara efektif domain angka "kompleks").
ISignedNumber<TSelf> Mengekspos API yang umum untuk semua jenis angka yang ditandatangani (seperti konsep NegativeOne).
IUnsignedNumber<TSelf> Mengekspos API umum untuk semua tipe bilangan tak bertanda.
IAdditiveIdentity<TSelf,TResult> Mengungkapkan konsep (x + T.AdditiveIdentity) == x.
IMinMaxValue<TSelf> Mengekspos konsep T.MinValue dan T.MaxValue.
IMultiplicativeIdentity<TSelf,TResult> Mengungkapkan konsep (x * T.MultiplicativeIdentity) == x.

1Jenis floating-point biner adalah Double (double), Half, dan Single (float).

2Tipe bilangan bulat biner adalah Byte (byte), Int16 (short), Int32 (int), Int64 (long), Int128, IntPtr (nint), SByte (sbyte), UInt16 (ushort), UInt32 (uint), UInt64 (ulong), UInt128, dan UIntPtr (nuint).

Antarmuka yang paling mungkin Anda gunakan secara langsung adalah INumber<TSelf>, yang kira-kira sesuai dengan angka riil . Jika jenis mengimplementasikan antarmuka ini, itu berarti bahwa nilai memiliki tanda (ini termasuk unsigned jenis, yang dianggap positif) dan dapat dibandingkan dengan nilai lain dengan jenis yang sama. INumberBase<TSelf> menganugerah konsep yang lebih canggih, seperti bilangan kompleks dan imajiner , misalnya, akar kuadrat dari angka negatif. Antarmuka lain, seperti IFloatingPointIeee754<TSelf>, dibuat karena tidak semua operasi masuk akal untuk semua jenis angka—misalnya, menghitung lantai angka hanya masuk akal untuk jenis floating-point. Di pustaka kelas dasar .NET, jenis Double floating-point mengimplementasikan IFloatingPointIeee754<TSelf> tetapi Int32 tidak.

Beberapa antarmuka juga diimplementasikan oleh berbagai jenis lain, termasuk Char, , DateOnly, DateTimeDateTimeOffset, Decimal, Guid, TimeOnly, , dan TimeSpan.

Tabel berikut menunjukkan beberapa API inti yang diekspos oleh setiap antarmuka.

Antarmuka Nama API Deskripsi
IBinaryInteger<TSelf> DivRem Menghitung kuota dan sisanya secara bersamaan.
LeadingZeroCount Menghitung jumlah bit nol di depannya dalam representasi biner.
PopCount Menghitung jumlah bit yang ditetapkan dalam representasi biner.
RotateLeft Memutar bit ke kiri, yang kadang-kadang juga disebut pergeseran melingkar ke kiri.
RotateRight Memutar bit ke kanan, kadang-kadang juga disebut shift kanan melingkar.
TrailingZeroCount Menghitung jumlah bit nol berikutnya dalam representasi biner.
IFloatingPoint<TSelf> Ceiling Membulatkan nilai ke arah tak terbatas positif. +4.5 menjadi +5, dan -4.5 menjadi -4.
Floor Membulatkan nilai ke arah tak terbatas negatif. +4.5 menjadi +4, dan -4.5 menjadi -5.
Round Membulatkan nilai menggunakan mode pembulatan yang ditentukan.
Truncate Membulatkan nilai menuju nol. +4.5 menjadi +4, dan -4.5 menjadi -4.
IFloatingPointIeee754<TSelf> E Mendapatkan nilai yang mewakili nomor Euler untuk jenis tersebut.
Epsilon Mendapatkan nilai terkecil yang dapat diwakili yang lebih besar dari nol untuk tipe tersebut.
NaN Mendapatkan nilai yang mewakili NaN untuk tipe tersebut.
NegativeInfinity Mendapatkan nilai yang mewakili -Infinity untuk tipe tersebut.
NegativeZero Mendapatkan nilai yang mewakili -Zero untuk tipe tersebut.
Pi Mendapatkan nilai yang mewakili Pi untuk tipe tersebut.
PositiveInfinity Mendapatkan nilai yang mewakili +Infinity untuk tipe tersebut.
Tau Mendapatkan nilai yang mewakili Tau (2 * Pi) untuk jenis tersebut.
(Lainnya) (Mengimplementasikan set lengkap antarmuka yang tercantum di bawah Antarmuka fungsi.)
INumber<TSelf> Clamp Membatasi nilai untuk tidak lebih dan tidak kurang dari nilai min dan maks yang ditentukan.
CopySign Mengatur tanda nilai yang ditentukan sama dengan nilai lain yang ditentukan.
Max Mengembalikan nilai yang lebih besar dari dua nilai, mengembalikan NaN jika salah satu input adalah NaN.
MaxNumber Mengembalikan nilai yang lebih besar dari dua nilai, mengembalikan angka jika satu input adalah NaN.
Min Mengembalikan lebih sedikit dari dua nilai, mengembalikan NaN jika salah satu input adalah NaN.
MinNumber Mengembalikan nilai yang lebih kecil dari dua nilai, mengembalikan angka jika salah satu input adalah NaN.
Sign Mengembalikan -1 untuk nilai negatif, 0 untuk nol, dan +1 untuk nilai positif.
INumberBase<TSelf> One Mendapatkan nilai 1 untuk jenis tersebut.
Radix Mendapatkan radix, atau basis, untuk tipe tersebut. Int32 mengembalikan 2. Decimal mengembalikan 10.
Zero Mendapatkan nilai 0 untuk jenis tersebut.
CreateChecked Membuat nilai, melemparkan OverflowException jika input tidak dapat cocok.1
CreateSaturating Membuat nilai, membatasi ke T.MinValue atau T.MaxValue jika input tidak dapat disesuaikan.1
CreateTruncating Membuat nilai dari nilai lain, menggulung kembali jika input tidak dapat muat. 1
IsComplexNumber Mengembalikan true jika nilai memiliki bagian riil bukan nol dan bagian imajiner bukan nol.
IsEvenInteger Mengembalikan true jika nilainya adalah bilangan bulat genap. 2.0 mengembalikan true, dan 2,2 mengembalikan false.
IsFinite Mengembalikan true jika nilainya tidak tak terbatas dan bukan NaN.
IsImaginaryNumber Mengembalikan benar jika nilai memiliki bagian nyata yang bernilai nol. Ini berarti 0 adalah imajiner dan 1 + 1i tidak.
IsInfinity Mengembalikan nilai benar jika nilai tersebut mewakili infinitas.
IsInteger Mengembalikan true jika nilainya adalah bilangan bulat. 2.0 dan 3.0 mengembalikan true, dan 2,2 dan 3,1 mengembalikan false.
IsNaN Mengembalikan true jika nilai mewakili NaN.
IsNegative Mengembalikan true jika nilainya negatif. Ini termasuk -0.0.
IsPositive Mengembalikan true jika nilainya positif. Ini termasuk 0 dan +0.0.
IsRealNumber Menghasilkan nilai true jika bagian imajiner dari nilai tersebut adalah nol. Ini berarti 0 sama nyatanya dengan semua jenis INumber<T>.
IsZero Mengembalikan true jika nilai mewakili nol. Ini termasuk 0, +0.0, dan -0.0.
MaxMagnitude Mengembalikan nilai dengan nilai absolut yang lebih besar, mengembalikan NaN jika salah satu input adalah NaN.
MaxMagnitudeNumber Mengembalikan nilai dengan nilai absolut yang lebih besar, mengembalikan angka jika satu input adalah NaN.
MinMagnitude Mengembalikan nilai dengan nilai absolut yang lebih rendah, mengembalikan NaN jika salah satu input adalah NaN.
MinMagnitudeNumber Mengembalikan nilai dengan nilai absolut yang lebih kecil, mengembalikan angka jika satu input adalah NaN.
ISignedNumber<TSelf> NegativeOne Mendapatkan nilai -1 untuk jenis tersebut.

1Untuk membantu memahami perilaku tiga Create* metode, pertimbangkan contoh berikut.

Contoh saat diberi nilai yang terlalu besar:

  • byte.CreateChecked(384) akan menghasilkan OverflowException.
  • byte.CreateSaturating(384) mengembalikan 255 karena 384 lebih besar dari Byte.MaxValue (yaitu 255).
  • byte.CreateTruncating(384) mengembalikan 128 karena membutuhkan 8 bit terendah (384 memiliki representasi heksa , 0x0180dan 8 bit terendah adalah 0x80, yaitu 128).

Contoh saat diberi nilai yang terlalu kecil:

  • byte.CreateChecked(-384) akan menghasilkan OverflowException.
  • byte.CreateSaturating(-384) mengembalikan 0 karena -384 lebih kecil dari Byte.MinValue (yaitu 0).
  • byte.CreateTruncating(-384) mengembalikan 128 karena membutuhkan 8 bit terendah (384 memiliki representasi heksa , 0xFE80dan 8 bit terendah adalah 0x80, yaitu 128).

Metode Create* juga memiliki beberapa pertimbangan khusus untuk jenis floating-point IEEE 754, seperti float dan double, karena mereka memiliki nilai khusus PositiveInfinity, NegativeInfinity, dan NaN. Ketiga API Create* berfungsi sebagai CreateSaturating. Selain itu, sementara MinValue dan MaxValue mewakili angka "normal" negatif/positif terbesar, nilai minimum dan maksimum aktual adalah NegativeInfinity dan PositiveInfinity, sehingga mereka menggunakan nilai-nilai ini sebagai batas.

Antarmuka operator

Antarmuka operator sesuai dengan berbagai operator yang tersedia untuk bahasa C#.

  • Mereka secara eksplisit tidak menggabungkan operasi seperti perkalian dan pembagian karena tidak sesuai dengan semua tipe. Misalnya, Matrix4x4 * Matrix4x4 valid, tetapi Matrix4x4 / Matrix4x4 tidak valid.
  • Mereka biasanya memungkinkan jenis input dan hasil berbeda untuk mendukung skenario seperti membagi dua bilangan bulat untuk mendapatkan double, misalnya, 3 / 2 = 1.5, atau menghitung rata-rata satu set bilangan bulat.
Nama antarmuka Operator yang ditentukan
IAdditionOperators<TSelf,TOther,TResult> x + y
IBitwiseOperators<TSelf,TOther,TResult> x & y, 'x | y', x ^ y, dan ~x
IComparisonOperators<TSelf,TOther,TResult> x < y, x > y, x <= y, dan x >= y
IDecrementOperators<TSelf> --x dan x--
IDivisionOperators<TSelf,TOther,TResult> x / y
IEqualityOperators<TSelf,TOther,TResult> x == y dan x != y
IIncrementOperators<TSelf> ++x dan x++
IModulusOperators<TSelf,TOther,TResult> x % y
IMultiplyOperators<TSelf,TOther,TResult> x * y
IShiftOperators<TSelf,TOther,TResult> x << y dan x >> y
ISubtractionOperators<TSelf,TOther,TResult> x - y
IUnaryNegationOperators<TSelf,TResult> -x
IUnaryPlusOperators<TSelf,TResult> +x

Nota

Beberapa antarmuka menentukan operator yang diperiksa selain operator reguler yang tidak dicentang. Operator yang diperiksa dipanggil dalam konteks yang diperiksa dan memungkinkan jenis yang ditentukan pengguna untuk menentukan perilaku luapan. Jika Anda menerapkan operator yang diperiksa, misalnya, CheckedSubtraction(TSelf, TOther), Anda juga harus menerapkan operator yang tidak dicentang, misalnya, Subtraction(TSelf, TOther).

Antarmuka fungsi

Antarmuka fungsi menentukan API matematika umum yang berlaku lebih luas daripada antarmuka numerik tertentu. Antarmuka ini semuanya diimplementasikan oleh IFloatingPointIeee754<TSelf>, dan dapat diimplementasikan oleh jenis lain yang relevan di masa depan.

Nama antarmuka Deskripsi
IExponentialFunctions<TSelf> Mengekspos fungsi eksponensial yang mendukung e^x, , e^x - 1, 2^x2^x - 1, 10^x, dan 10^x - 1.
IHyperbolicFunctions<TSelf> Mengekspos fungsi-fungsi hiperbolik yang mendukung acosh(x), asinh(x), atanh(x), cosh(x), sinh(x), dan tanh(x).
ILogarithmicFunctions<TSelf> Mengekspos fungsi logaritmik yang mendukung ln(x), , ln(x + 1), log2(x)log2(x + 1), log10(x), dan log10(x + 1).
IPowerFunctions<TSelf> Mengekspos fungsi kekuatan yang mendukung x^y.
IRootFunctions<TSelf> Mengekspos fungsi dasar yang mendukung cbrt(x) dan sqrt(x).
ITrigonometricFunctions<TSelf> Mengekspos fungsi trigonometrik yang mendukung acos(x), asin(x), atan(x), cos(x), sin(x), dan tan(x).

Mengurai dan memformat antarmuka

Penguraian dan pemformatan adalah konsep inti dalam pemrograman. Mereka biasanya digunakan saat mengonversi input pengguna ke jenis tertentu atau menampilkan jenis kepada pengguna. Antarmuka ini berada di System namespace.

Nama antarmuka Deskripsi
IParsable<TSelf> Mengekspos dukungan untuk T.Parse(string, IFormatProvider) dan T.TryParse(string, IFormatProvider, out TSelf).
ISpanParsable<TSelf> Mengekspos dukungan untuk T.Parse(ReadOnlySpan<char>, IFormatProvider) dan T.TryParse(ReadOnlySpan<char>, IFormatProvider, out TSelf).
IFormattable 1 Mengekspos dukungan untuk value.ToString(string, IFormatProvider).
ISpanFormattable 1 Mengekspos dukungan untuk value.TryFormat(Span<char>, out int, ReadOnlySpan<char>, IFormatProvider).

1Antarmuka ini bukan baru, juga tidak umum. Namun, ini diimplementasikan oleh semua jenis angka dan mewakili operasi terbalik dari IParsable.

Misalnya, program berikut mengambil dua angka sebagai input, membacanya dari konsol menggunakan metode generik di mana parameter jenis dibatasi menjadi IParsable<TSelf>. Ini menghitung rata-rata menggunakan metode generik di mana parameter jenis untuk nilai input dan hasil dibatasi menjadi INumber<TSelf>, lalu menampilkan hasilnya ke konsol.

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

Lihat juga