泛型数学

.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 中的接口,这些接口描述类似数字的类型及其可用的功能。

接口名称 DESCRIPTION
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.MinValueT.MaxValue的概念。
IMultiplicativeIdentity<TSelf,TResult> 公开 (x * T.MultiplicativeIdentity) == x 的概念。

1二进制 浮点类型DoubledoubleHalfSinglefloat)。

2 二进制 整数类型Bytebyte)、Int16short)、Int32int)、Int64long)、Int128IntPtrnint)、SBytesbyte)、UInt16ushort)、UInt32uint)、UInt64ulong)、UInt128UIntPtrnuint)。

你最有可能直接使用的接口是 INumber<TSelf>,它大致对应于 数。 如果某个类型实现此接口,则表示值具有一个符号(这包括 unsigned 被视为正类型的类型),并且可以与其他相同类型的值进行比较。 INumberBase<TSelf> 提供更高级的概念,例如 数和 数,例如负数的平方根。 由于并非所有操作对所有数字类型都有意义,因此创建了其他接口,例如IFloatingPointIeee754<TSelf>。例如,计算一个数的下限只有在浮点类型中才有意义。 在 .NET 基类库中,浮点类型Double实现了IFloatingPointIeee754<TSelf>,但不实现Int32

这些接口还由各种其他类型的实现,包括CharDateOnlyDateTimeDateTimeOffsetDecimalGuidTimeOnlyTimeSpan

下表显示了每个接口公开的一些核心 API。

接口 API 名称 DESCRIPTION
IBinaryInteger<TSelf> DivRem 同时计算商和余数。
LeadingZeroCount 计算二进制表示形式的前导零位数。
PopCount 计算二进制表示中的设置位数。
RotateLeft 向左旋转位,有时也称为循环左移。
RotateRight 向右旋转位,有时也称为循环右移。
TrailingZeroCount 计算二进制表示中的尾随零位数。
IFloatingPoint<TSelf> Ceiling 将值向正无穷方向舍入。 +4.5 变为 +5,-4.5 变为 -4。
Floor 将值向负无穷方向舍入。 +4.5 变为 +4,-4.5 变为 -5。
Round 使用指定的舍入模式对值进行舍入。
Truncate 将值向零舍入。 +4.5 变为 +4,-4.5 变为 -4。
IFloatingPointIeee754<TSelf> E 获取一个值,该值表示该类型的欧拉数。
Epsilon 获取该类型的大于零的最小可表示值。
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 表示零,+1 表示正值。
INumberBase<TSelf> One 获取类型的值 1。
Radix 获取类型的基数。 Int32 返回 2。 Decimal 返回 10。
Zero 获取类型的值 0。
CreateChecked 创建一个值,如果输入不合适,则引发 OverflowException1
CreateSaturating 创建一个值,将其限制在 T.MinValueT.MaxValue 之间,如果输入无法符合。1
CreateTruncating 从一个值创建另一个值,如果输入不适合,则环绕处理。1
IsComplexNumber 如果值具有非零实部分和非零虚部分,则返回 true。
IsEvenInteger 如果值为偶数整数,则返回 true。 2.0 返回 true,2.2 返回 false
IsFinite 如果值不是无限值,并且不是NaN,则返回 true。
IsImaginaryNumber 如果值为零实部分,则返回 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 如果值为零虚部分,则返回 true。 这意味着 0 与所有类型的 INumber<T> 类型一样真实。
IsZero 如果值表示零,则返回 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
  • byte.CreateSaturating(384) 返回 255,因为 384 大于 Byte.MaxValue (即 255)。
  • byte.CreateTruncating(384) 返回 128,因为它采用最低的 8 位(384 具有十六进制表示形式 0x0180,最低 8 位 0x80为 128)。

给定值过小时的示例:

  • byte.CreateChecked(-384) 将引发 OverflowException
  • byte.CreateSaturating(-384) 返回 0,因为 -384 小于 Byte.MinValue (即 0)。
  • byte.CreateTruncating(-384) 返回 128,因为它采用最低的 8 位(384 具有十六进制表示形式 0xFE80,最低 8 位 0x80为 128)。

这些 Create* 方法还对 IEEE 754 浮点类型有一些特殊注意事项,像 floatdouble 类似的,具有特殊值 PositiveInfinityNegativeInfinity以及 NaN。 所有三个 Create* API 的行为方式如同 CreateSaturating。 此外,虽然 MinValueMaxValue 表示最大的负/正“正常”数字,但实际最小值和最大值是 NegativeInfinityPositiveInfinity因此它们会固定到这些值。

操作员界面

运算符接口对应于可用于 C# 语言的各种运算符。

  • 它们明确地不对乘法和除法等操作进行配对,因为这并非对所有类型都正确。 例如, Matrix4x4 * Matrix4x4 有效,但 Matrix4x4 / Matrix4x4 无效。
  • 它们通常允许输入和结果类型有所不同,以支持方案,例如,将两个整数除以获取一个double3 / 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 < yx > yx <= yx >= y
IDecrementOperators<TSelf> --xx--
IDivisionOperators<TSelf,TOther,TResult> x / y
IEqualityOperators<TSelf,TOther,TResult> x == yx != y
IIncrementOperators<TSelf> ++xx++
IModulusOperators<TSelf,TOther,TResult> x % y
IMultiplyOperators<TSelf,TOther,TResult> x * y
IShiftOperators<TSelf,TOther,TResult> x << yx >> y
ISubtractionOperators<TSelf,TOther,TResult> x - y
IUnaryNegationOperators<TSelf,TResult> -x
IUnaryPlusOperators<TSelf,TResult> +x

注释

某些接口除了定义一个常规的未检查运算符外,还定义了一个检查过的运算符。 选中的运算符在已检查的上下文中调用,并允许用户定义的类型定义溢出行为。 如果实现了 checked 运算符,例如 CheckedSubtraction(TSelf, TOther),则还必须实现 unchecked 的运算符,例如 Subtraction(TSelf, TOther)

函数接口

函数接口定义比应用于特定 数值接口更广泛的常见数学 API。 这些接口都是由IFloatingPointIeee754<TSelf>实现的,将来可能会由其他相关类型实现。

接口名称 DESCRIPTION
IExponentialFunctions<TSelf> e^xe^x - 12^x2^x - 110^x10^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

接口名称 DESCRIPTION
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
*/

另请参阅