제네릭 수학
.NET 7에서는 기본 클래스 라이브러리에 새로운 수학 관련 제네릭 인터페이스를 도입합니다. 이러한 인터페이스를 사용할 수 있다는 것은 제네릭 형식 또는 메서드의 형식 매개 변수를 "숫자 유사"로 제한할 수 있음을 의미합니다. 또한 C# 11 이상에서는 static virtual
인터페이스 멤버를 정의할 수 있습니다. 연산자는 static
으로 선언되어야 하므로 이 새로운 C# 기능을 사용하면 숫자와 유사한 형식에 대한 새 인터페이스에서 연산자를 선언할 수 있습니다.
이러한 혁신을 통해 작업 중인 정확한 형식을 알 필요 없이 일반적으로 수학 연산을 수행할 수 있습니다. 예를 들어, 두 개의 숫자를 추가하는 메서드를 작성하려는 경우 이전에는 각 형식(예: static int Add(int first, int second)
및 static float Add(float first, float second)
)에 대한 메서드의 오버로드를 추가해야 했습니다. 이제 형식 매개 변수가 숫자와 유사한 형식으로 제한되는 단일 제네릭 메서드를 작성할 수 있습니다. 예시:
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> | 모든 부호 있는 숫자 형식(예: NegativeOne 의 개념)에 공통적인 API를 노출합니다. |
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, DateTime, DateTimeOffset, Decimal, Guid, TimeOnly 및 TimeSpan을 포함한 다양한 다른 형식으로도 구현됩니다.
다음 표에서는 각 인터페이스에서 제공하는 핵심 API 중 일부를 보여 줍니다.
인터페이스 | API 이름 | 설명 |
---|---|---|
IBinaryInteger<TSelf> | DivRem |
몫과 나머지를 동시에 계산합니다. |
LeadingZeroCount |
이진 표현에서 선행 0 비트 수를 셉니다. | |
PopCount |
이진 표현에서 설정된 비트 수를 셉니다. | |
RotateLeft |
비트를 왼쪽으로 회전합니다. 순환 왼쪽 시프트라고도 합니다. | |
RotateRight |
비트를 오른쪽으로 회전합니다. 순환 오른쪽 시프트라고도 합니다. | |
TrailingZeroCount |
이진 표현에서 후행 0비트의 수를 셉니다. | |
IFloatingPoint<TSelf> | Ceiling |
값을 양의 무한대로 반올림합니다. +4.5는 +5가 되고, -4.5는 -4가 됩니다. |
Floor |
값을 음의 무한대로 반올림합니다. +4.5는 +4가 되고, -4.5는 -5가 됩니다. | |
Round |
지정된 반올림 모드를 사용하여 값을 반올림합니다. | |
Truncate |
값을 0에 가깝게 반올림합니다. +4.5는 +4가 되고, -4.5는 -4가 됩니다. | |
IFloatingPointIeee754<TSelf> | E |
형식에 대한 오일러 수를 나타내는 값을 가져옵니다. |
Epsilon |
해당 형식에 대해 0보다 큰 표현 가능한 가장 작은 값을 가져옵니다. | |
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이면 0, 양수 값이면 +1을 반환합니다. | |
INumberBase<TSelf> | One |
형식에 대해 값 1을 가져옵니다. |
Radix |
형식에 대한 기수 또는 베이스를 가져옵니다. Int32가 2를 반환합니다. Decimal은 10을 반환합니다. | |
Zero |
형식에 대해 값 0을 가져옵니다. | |
CreateChecked |
입력이 맞지 않으면 OverflowException을 throw하여 값을 만듭니다.1 | |
CreateSaturating |
입력이 맞지 않으면 T.MinValue 또는 T.MaxValue 로 고정하여 값을 만듭니다.1 |
|
CreateTruncating |
다른 값에서 값을 만들고 입력이 맞지 않으면 래핑합니다.1 | |
IsComplexNumber |
값에 0이 아닌 실수 부분과 0이 아닌 허수 부분이 있는 경우 true를 반환합니다. | |
IsEvenInteger |
값이 짝수이면 true를 반환합니다. 2.0은 true 를 반환하고 2.2는 false 를 반환합니다. |
|
IsFinite |
값이 무한하지 않고 NaN 이 아닌 경우 true를 반환합니다. |
|
IsImaginaryNumber |
값에 실수부가 0인 경우 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 |
값에 허수부가 0인 경우 true를 반환합니다. 이는 모든 INumber<T> 형식과 마찬가지로 0이 실수임을 의미합니다. |
|
IsZero |
값이 0을 나타내는 경우 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을 throw합니다.- 384가 Byte.MaxValue(255)보다 크므로
byte.CreateSaturating(384)
는 255를 반환합니다. byte.CreateTruncating(384)
는 가장 낮은 8비트를 사용하므로 128을 반환합니다(384는0x0180
의 16진수 표현을 가지며 가장 낮은 8비트는0x80
, 즉 128입니다).
너무 작은 값이 지정된 경우의 예:
byte.CreateChecked(-384)
는 OverflowException을 throw합니다.- -384가 Byte.MinValue(0)보다 작기 때문에
byte.CreateSaturating(-384)
는 0을 반환합니다. byte.CreateTruncating(-384)
는 가장 낮은 8비트를 사용하므로 128을 반환합니다(384는0xFE80
의 16진수 표현을 가지며 가장 낮은 8비트는0x80
, 즉 128입니다).
Create*
메서드에는 특수 값 PositiveInfinity
, NegativeInfinity
및 NaN
이 있으므로 float
및 double
과 같은 IEEE 754 부동 소수점 형식에 대한 몇 가지 특별한 고려 사항도 있습니다. 세 가지 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) 에 대한 지원을 노출합니다. |
IFormattable1 | value.ToString(string, IFormatProvider) 에 대한 지원을 노출합니다. |
ISpanFormattable1 | 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
*/
참고 항목
.NET