将 C# 中的 INumber<T> 转换为浮点数? .net 7 或更高版本

问题描述 投票:0回答:1

有没有办法将整数转换为适当的浮点数并返回?一些获取和返回整数的函数在计算过程中需要浮点数。

看这个:

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# generics .net-generic-math
1个回答
0
投票

您想要做的是,在 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<>
    的泛型接口。 由于这可能很慢,因此我静态缓存了结果。

© www.soinside.com 2019 - 2024. All rights reserved.