将数字四舍五入到最近倍数的通用C#方法?(不是重复的,有特定的角度和建议的答案)

将数字四舍五入到最近倍数的通用C#方法?(不是重复的,有特定的角度和建议的答案),c#,numerical-methods,C#,Numerical Methods,我有一个C#扩展方法,如下所示: public static double RoundOff(this double rawNumber, double roundToNearest) { double rawMultiples = rawNumber / roundToNearest; double roundedMultiples = Math.Round(rawMultiples); double roundedNumber = roundedMult

我有一个C#扩展方法,如下所示:

public static double RoundOff(this double rawNumber, double roundToNearest)
{
    double rawMultiples     = rawNumber / roundToNearest;
    double roundedMultiples = Math.Round(rawMultiples);
    double roundedNumber    = roundedMultiples * roundToNearest;
    return roundedNumber;
}
我不想为所有不同的数字类型(int、decimal等)编写多次

有没有一种方法可以这样做

public static double RoundOff<T>(this T rawNumber, T roundToNearest)
    where T : [SOME CONSTRAINT]
{
    T rawMultiples     = rawNumber / roundToNearest;
    T roundedMultiples = Math.Round(rawMultiples);
    T roundedNumber    = roundedMultiples * roundToNearest;
    return roundedNumber;
}
公共静态双舍入(此T rawNumber,T RONDTONEARest)
其中T:[一些约束]
{
T rawMultiples=rawNumber/roundToNearest;
T roundedMultiples=数学圆(整数倍);
T roundedNumber=roundedMultiples*roundToNearest;
返回舍入数;
}
如果能够更普遍地做到这一点,那将非常有用。要维护的代码更少,而且只需一个扩展方法就可以提供更多的功能

如果不能做到,是不是因为C#不能以这种方式扩展到工作?或者有一天它会被扩展到允许“所有数字类型”通用约束吗

欢迎任何想法

更新
回答关于与另一个问题相似性的问题。是的,它在主题方面是相似的,但不同,因为我追求的是一个特定问题的特定解决方案。我已经在下面添加了我自己的尝试性回答以澄清。如果有人仍然认为我没有抓住要点,欢迎挑战。

显然不可能像其他评论员所说的那样,对泛型做你想做的事情,但由于所有数字都可以转换为双倍,你至少可以减少逻辑重复:

public static double RoundOff(this long rawNumber, double roundToNearest)
{
    return RoundOff((double) rawNumber, roundToNearest);
}

public static double RoundOff(this double rawNumber, double roundToNearest)
{
    T rawMultiples     = rawNumber / roundToNearest;
    T roundedMultiples = Math.Round(rawMultiples);
    T roundedNumber    = roundedMultiples * roundToNearest;
    return roundedNumber;
}
如果您不使用扩展方法进行管理,那么您可以直接对任何数字调用此方法,因为C#将自动将所有类型转换为double

int i = 5;
RoundOff(i, 0.5);

我承认这个问题与其他问题有一些相似之处,但我的问题是关于解决一个非常具体的问题的非常具体的问题

我想在这里分享我自己的解决方案。从好的方面来说,它是完全通用的。在负数方面,由于使用小数,速度较慢,但如果需要,可以调整为更快的双倍数

    public static T RoundOff<T>(this T rawNumber, T roundToNearest)
        where T : IComparable<T>
    {
        if (typeof(T).IsNumericType())
        {
            decimal decimalRoundToNearest   = Convert.ToDecimal(roundToNearest);
            decimal rawMultiples            = Convert.ToDecimal(rawNumber) / Convert.ToDecimal(roundToNearest);
            decimal decimalRoundedMultiples = Math.Round(rawMultiples);
            decimal decimalRoundedNumber    = decimalRoundedMultiples * decimalRoundToNearest;

            return (T)Convert.ChangeType(decimalRoundedNumber, typeof(T));
            // alternative
            // TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
            // return (T)converter.ConvertFrom(decimalRoundedNumber);

        }
        else
        {
            throw new Exception("Type " + typeof(T) + " is not numeric");
        }
    }
它并非没有缺点,但它确实符合我的要求

一个自我批评:因为我们正在舍入,小数的额外精度可能是不必要的——但直觉上,使用高精度类型似乎更好。我可能在这一点上是错的,也许一些更有知识的人可以提供一些见解


因此,我希望这能帮助某人,或激起愤怒的反应,如雪崩般,如何才能做得更好

不存在“数值类型”通用约束。你必须为你想要支持的每一种类型创建重载。你甚至不能声明一个类型参数约束说“这个类型必须支持这些数学运算符”。有些人通过重新编译编译代码来绕过这个问题。可能的副本可能会考虑使用一个T4模板,它可以用特定类型名称替换这个方法,然后使用它来支持<代码>数学类型。无论如何-因此没有必要做更多的事情(int和
long
应该直接调用
十进制
一)。而且您的代码对
int
无论如何都不起作用,因为
/
进行整数除法(换句话说,您不能以通用方式在数字类型中使用代码,因为它对某些类型不起作用)。但是,您可能会对
long
失去精度。不确定您的意思是什么,Max long在Max double?double的精度只有52/53位。朗有64岁。这意味着两个相邻的大多头很可能被转换成相同的双倍值。可能没什么大不了的,因为它被四舍五入:)此外,还有一个除法无论如何都不能返回多头。但你可能是对的,在一些特殊情况下,它可能会导致一个错误,当然这是一个有用的想法,但我仍然觉得必须有一个“一刀切”的解决方案。我自己的尝试实现了这一点,尽管有一些自我意识问题。添加了一些有用的参考资料,有人在听吗?有人对提议的答案有想法吗?
    public static bool IsNumericType(this object o)
    {
        // https://stackoverflow.com/questions/1749966/c-sharp-how-to-determine-whether-a-type-is-a-number
        switch (Type.GetTypeCode(o.GetType()))
        {
            case TypeCode.Byte:
            case TypeCode.SByte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
            case TypeCode.Decimal:
            case TypeCode.Double:
            case TypeCode.Single:
                return true;

            default:
                return false;
        }
    }