C# double和decimal数据类型以截断的方式保留指定的小数位数


在C#中,如果你想以截断的方式(即直接去除多余的小数位,不进行四舍五入)保留指定的小数位数,你可以使用`Math.Truncate`函数结合乘以和除以10的幂次来实现。但是,`Math.Truncate`本身只适用于整数或截断为整数,因此我们需要对`double`或`decimal`值进行一些操作。

对于`double`类型,我们可以先将其乘以10的指定小数位数次幂,截断后,再除以相同的幂次。但需要注意的是,由于`double`的精度问题,这种方法可能不会在所有情况下都精确。

对于`decimal`类型,我们可以使用`Decimal.Truncate`方法,但这个方法需要我们先将`decimal`值乘以10的幂次后转换为整数(`long`),截断后再转回`decimal`并除以10的幂次。但更直接和简单的方法是使用`Decimal.Round`方法并指定中点舍入模式为`MidpointRounding.AwayFromZero`(虽然这不是纯粹的截断,但在这个特定场景下,如果小数部分恰好是.5,它会向上或向下舍入,对于截断目的来说,在大多数情况下是“足够接近”的)或者通过自定义逻辑来实现截断。

不过,为了直接截断,我们可以不使用`Decimal.Round`,而是采用以下方式:


using System;

class Program
{
    static decimal TruncateDecimal(decimal value, int decimals)
    {
        decimal factor = (decimal)Math.Pow(10, decimals);
        return Math.Truncate(value * factor) / factor;
        // 注意:这里实际上对于decimal类型,使用Math.Truncate并不是最佳选择,
        // 因为Math.Truncate是针对double的,并且会进行类型转换。
        // 更准确的方法是使用Decimal.Truncate,但Decimal.Truncate只能截断到整数。
        // 因此,上面的代码更多是为了演示思路,对于decimal类型,我们通常使用下面的方法:

        // 更适用于decimal的截断方式
        // return (decimal)Math.Floor(value * factor) / factor;
        // 或者更直接地,避免使用Math类(因为Math是针对double的):
        // return (value * (decimal)Math.Pow(10, decimals)) - ((value * (decimal)Math.Pow(10, decimals)) % 1) / factor;
        // 但上面的表达式可能不是最高效的,一个更简洁且效率更高的方法是:
        return (long)(value * factor) / factor;
    }

    static void Main()
    {
        decimal number = 123.456789m;
        int decimalsToKeep = 3;
        decimal truncated = TruncateDecimal(number, decimalsToKeep);
        Console.WriteLine(truncated); // 输出: 123.456
    }
}

注意:在上面的`TruncateDecimal`方法中,我提供了几种不同的实现方式,但请注意,对于`decimal`类型,直接使用`(long)(value * factor) / factor`这种转换和除法的方式是最简洁且效率较高的,因为它直接避免了不必要的浮点数运算和精度问题。