将数字截断到指定的小数位数

本文关键字:小数 数字 | 更新日期: 2023-09-27 18:22:21

我需要将一个数字截断到小数点后2位,这基本上意味着切掉多余的数字。

例如:

2.919     ->      2.91
2.91111   ->     2.91

为什么?这就是SQL server在存储特别精确。例如,如果一列是Decimal(8,2),并且您尝试插入/更新编号9.1234,则3和4将被截断。

我需要在c#代码中做完全相同的事情。

我能想到的唯一可能的方法是:

  1. 仅使用字符串格式器"打印"出来两位小数,然后将其转换为十进制,例如:

      decimal tooManyDigits = 2.1345
    decimal ShorterDigits = Convert.ToDecimal(tooManyDigits.ToString("0.##"));
    // ShorterDigits is now 2.13
    

    我对此不满意,因为它涉及到一个to字符串,然后另一个字符串到十进制的转换似乎有点疯狂。

  2. 使用Math.Ttruncate(只接受整数),所以我可以将它乘以100,截断它,然后除以100。例如:

    decimal tooLongDecimal = 2.1235;
    tooLongDecimal = Math.Truncate(tooLongDecimal * 100) / 100;
    

    我对此也不满意,因为如果tooLongDecimal为0,我会得到一个被0除的错误。

当然还有更好更简单的方法!有什么建议吗?

将数字截断到指定的小数位数

您自己已经回答了这个问题;你好像误解了零除法的意思。做到这一点的正确方法是相乘、截断,然后进行devide,如下所示:

decimal TruncateTo100ths(decimal d)
{
    return Math.Truncate(d* 100) / 100;
}
TruncateTo100ths(0m);       // 0
TruncateTo100ths(2.919m);   // 2.91
TruncateTo100ths(2.91111m); // 2.91
TruncateTo100ths(2.1345m);  // 2.13

这里没有除以零,只有除以100,这是完全安全的。

以前提供的数学解决方案很容易被大量数字和/或小数位数溢出。考虑以下扩展方法:

public static decimal TruncateDecimal(this decimal d, int decimals)
{
    if (decimals < 0)
        throw new ArgumentOutOfRangeException("decimals", "Value must be in range 0-28."); 
    else if (decimals > 28)
        throw new ArgumentOutOfRangeException("decimals", "Value must be in range 0-28.");
    else if (decimals == 0)
        return Math.Truncate(d);
    else
    {
        decimal integerPart = Math.Truncate(d);
        decimal scalingFactor = d - integerPart;
        decimal multiplier = (decimal) Math.Pow(10, decimals);
        scalingFactor = Math.Truncate(scalingFactor * multiplier) / multiplier;
        return integerPart + scalingFactor;
    }
}

用法:

decimal value = 18446744073709551615.262626263m;
value = value.TruncateDecimal(6); // Result: 18446744073709551615.262626

我同意p.s.w.g.我也有类似的要求,下面是我的经验和一个更通用的截断函数。

http://snathani.blogspot.com/2014/05/truncating-number-to-specificnumber-of.html

public static decimal Truncate(decimal value, int decimals)
{
    decimal factor = (decimal)Math.Pow(10, decimals);
    decimal result = Math.Truncate(factor * value) / factor;
    return result;
}

使用decimal.ToString('0.##')强制舍入:

1.119M.ToString("0.##")  // -> 1.12

(是的,可能应该是一个注释,但很难像这样格式化。)

public static decimal Rounding(decimal val, int precision)
    {
        decimal res = Trancating(val, precision + 1);
        return Math.Round(res, precision, MidpointRounding.AwayFromZero);
    }
    public static decimal Trancating(decimal val,int precision)
    {
        if (val.ToString().Contains("."))
        {
            string valstr = val.ToString();
            string[] valArr = valstr.Split('.');
            if(valArr[1].Length < precision)
            {
                int NoOfZeroNeedToAdd = precision - valArr[1].Length;
                for (int i = 1; i <= NoOfZeroNeedToAdd; i++)
                {
                    valstr = string.Concat(valstr, "0");
                }
            }
            if(valArr[1].Length > precision)
            {
                valstr = valArr[0] +"."+ valArr[1].Substring(0, precision);
            }
            return Convert.ToDecimal(valstr); 
        }
        else
        {
            string valstr=val.ToString();
            for(int i = 0; i <= precision; i++)
            {
                    if (i == 1)
                        valstr = string.Concat(valstr, ".0");
                    if(i>1)
                    valstr = string.Concat(valstr, "0");
            }
            return Convert.ToDecimal(valstr);
        }
        
    }