有没有办法将整数转换为适当的浮点数并返回?一些获取和返回整数的函数在计算过程中需要浮点数。
看这个:
void Main()
{
Interpolation.LinearBetween((0, 5), (10, 3), 7).DumpIt();
Interpolation.LinearBetween((0.0f, 5), (10, 3), 7).RoundToInt().DumpIt();
}
public static class Interpolation
{
public static T LinearBetween<T>((T x1, T y1) a, (T x2, T y2) b, T x) where T : INumber<T>
{
//f(x) = m*x + n
//one oft operands needs to be floating point type
var m = (b.y2 - a.y1) / (b.x2 - a.x1);
var n = b.y2 - m * b.x2;
return (x * m + n);
}
public static int RoundToInt(this float value)
{
return (int)Math.Round(value);
}
public static void DumpIt(this object toDump)
{
Console.WriteLine(toDump);
}
}
做整个 LinearBetween 是不正确的。 编辑:重写问题。
您想要做的是,在 C# 的通用数学的上下文中,当对某些任意数字类型
T
的值执行复杂计算时,当T
也恰好是通用二进制整数时,使用浮点精度执行计算,并且仅在最后四舍五入。
这是可能的,但很尴尬。 这是一种可能的方法:
public static class Interpolation
{
public static T LinearBetween<T>((T x1, T y1) a, (T x2, T y2) b, T x) where T : INumber<T> =>
LinearBetween<T, double>(a, b, x); // TODO: decide whether float is precise enough
public static T LinearBetween<T, TFloat>((T x1, T y1) a, (T x2, T y2) b, T x)
where T : INumber<T>, INumberBase<T>
where TFloat : IFloatingPoint<TFloat>
{
if (NumericTypeInformation<T>.IsBinaryInteger)
{
// An integer type. Convert to the preferred float format, perform the calculation, then round at the very end.
var floatResult = LinearBetweenUnconverted(
(TFloat.CreateChecked(a.x1), TFloat.CreateChecked(a.y1)),
(TFloat.CreateChecked(b.x2), TFloat.CreateChecked(b.y2)),
TFloat.CreateChecked(x));
var rounded = TFloat.Round(floatResult, MidpointRounding.ToEven); // TODO: choose your preferred MidpointRounding
return T.CreateChecked(rounded);
}
else
{
// Not an integer type. Perform the calculation directly.
return LinearBetweenUnconverted(a, b, x);
}
}
static T LinearBetweenUnconverted<T>((T x1, T y1) a, (T x2, T y2) b, T x) where T : INumber<T>
{
var m = (b.y2 - a.y1) / (b.x2 - a.x1);
var n = b.y2 - m * b.x2;
return (x * m + n);
}
}
// Generic class to static cache type information about a given numeric type.
public static class NumericTypeInformation<TNumber> where TNumber : INumberBase<TNumber>
{
readonly static Lazy<bool> isBinaryInteger = new Lazy<bool>(
() => typeof(TNumber)
.GetInterfaces()
.Where(i => i.IsGenericType)
.Select(i => i.GetGenericTypeDefinition())
.Any(i => i == typeof(IBinaryInteger<>)));
public static bool IsBinaryInteger => isBinaryInteger.Value;
}
演示小提琴在这里。
要点:
INumberBase<TSelf>.CreateChecked<TOther>(TOther)
从另一种类型的数字中创建一种类型的数字,例如从 float
或 double
创建 int
或 short
,反之亦然。 这个特殊的方法会在溢出时抛出异常。 如果您不想这样,您可以使用 CreateTruncating()
或 CreateSaturating()
将任意数字转换为某种浮点类型后,您可以使用该类型进行计算,然后使用
IFloatingPoint<TSelf>.Round(TSelf, MidpointRounding)
进行舍入,最后使用 CreateChecked()
反向转换回来。
我选择在
double
而不是float
中进行中间计算纯粹是出于个人喜好。
检查任意
INumber<T>
类型是否实现 IBinaryInteger<T>
非常困难! 我认为可以做类似的事情
static bool IsBinaryInteger<T>() where T : INumber<T> =>
typeof(T).IsAssignableTo(typeof(IBinaryInteger<T>);
但这会导致编译错误
类型“
”不能用作泛型类型或方法“T
”中的类型参数“TSelf
”。没有从 'IBinaryInteger<TSelf>
' 到 'T
' 的装箱转换或类型参数转换。System.Numerics.IBinaryInteger<T>
IBinaryInteger<TSelf>
有一个约束where TSelf : IBinaryInteger<TSelf>
——所以可能不可能检查它是否是类型TSelf
实现了IBinaryInteger<TSelf>
,除非它已经是被迫这样做!
作为替代方案,我必须循环遍历
T
实现的所有接口,并检查是否有开放泛型类型为 IBinaryInteger<>
的泛型接口。 由于这可能很慢,因此我静态缓存了结果。