HOWTO · Csharp

在 C# 中将 decimal 转换为 int

介绍在 C# 中将 decimal 转换为 int 时如何选择截断、最近整数舍入或定向舍入规则,并处理超出 Int32 范围的值。

本页内容

如果要丢弃小数部分,请使用 (int)value;如果要进行最近整数舍入,请使用 Convert.ToInt32(value);如果舍入规则是定向的,或有特定的中点策略,请使用 Math 方法。下面的紧凑示例使用正数比较这些选择。

using System;

class Program
{
    static void Main()
    {
        decimal value = 12.9m;
        Console.WriteLine("cast: " + (int)value);
        Console.WriteLine("nearest: " + Convert.ToInt32(12.5m) + ", " + Convert.ToInt32(13.5m));
        Console.WriteLine("floor: " + Math.Floor(value));
        Console.WriteLine("ceiling: " + Math.Ceiling(value));
        Console.WriteLine("away: " + Math.Round(2.5m, MidpointRounding.AwayFromZero));
    }
}
cast: 12
nearest: 12, 14
floor: 12
ceiling: 13
away: 3

转换策略是业务规则,而不是格式化细节。如果小数部分仍然有意义,例如金额或测量值,就应将值保留为 decimal。最终转换为 int 时,还必须处理不适合 Int32 的值。

将 decimal 向零截断

如果目标是截断,显式强制转换 (int)value 是最清晰的选择。decimal 字面量需要 m 后缀;没有后缀时,像 12.9 这样的字面量属于 double。将 decimal 转换为整数类型时会向零舍入:(int)12.9m 的结果是 12,而 (int)-12.9m 的结果是 -12。这既不是向下取整,也不是向上取整。C# 数值转换参考说明了向零行为和超出范围时的失败情况。

舍入到最接近的整数

Convert.ToInt32(decimal) 会舍入到最接近的 32 位有符号整数,而不是截断。当小数部分恰好为 .5 时,它采用中点向偶数舍入:Convert.ToInt32(12.5m) 的结果是 12,而 Convert.ToInt32(13.5m) 的结果是 14。这适用于需要将测量值舍入到最接近的完整单位的场景;但如果每个中点都必须远离零,这就不是正确的策略。

Convert.ToInt32 的 decimal 重载文档说明,超出 int 范围的值会引发 OverflowException。该方法不会让过大的 decimal 变得安全。

选择明确的舍入规则

先对 decimal 应用 Math 方法,再转换已经是整数的结果。Math.Floor 向负无穷大方向移动,因此 Math.Floor(-12.9m) 的结果是 -13m;Math.Ceiling 向正无穷大方向移动。因此,两者都不同于 (int)-12.9m 得到的 -12。

如果需要最近整数舍入并明确指定中点规则,请使用 Math.Round(value, MidpointRounding.AwayFromZero)。它会将 2.5m 变为 3m,将 -2.5m 变为 -3m。默认的 Math.Round(decimal) 使用中点向偶数舍入。Math.Truncate 也是先得到向零整数化的 decimal、再将其转换为 int 的一种清晰方式。

不要在舍入前进行强制转换:(int)value 已经丢弃了小数部分,之后再调用 Math.Round 无法恢复原本的选择。应先舍入,检查结果,然后再进行强制转换。

处理超出 int 范围的值

int 是一个由 int.MinValue 和 int.MaxValue 限定范围的 32 位有符号整数;Int32 是对应的框架类型。decimal 可以表示超出这些边界的值。当结果超出该范围时,decimal 到 int 的转换会引发 OverflowException;它不会将值限制到某个端点。

如果输入可能超出范围,请在转换前验证,或在应用程序边界捕获 OverflowException。checked 转换可以明确表达意图,也有助于测试范围约定,但它不是检测 decimal 到 int 转换溢出的机制。如果接收方契约允许,请保留 decimal 或选择更宽的整数类型,而不要默默地将值强制放入 int。

选择符合规则的转换

目标结果 使用方式 重要行为
丢弃小数部分 (int)value 向零截断。
最接近的 int Convert.ToInt32(value) 中点向偶数舍入。
始终向下或向上 Math.Floor 或 Math.Ceiling,然后强制转换 对负数而言,定向舍入不同于截断。
自定义中点行为 使用 MidpointRounding 的 Math.Round,然后强制转换 明确写出中点规则。

普通截断应选择显式强制转换。只有当 Convert.ToInt32 或 Math 方法的舍入策略正是应用程序所需的规则时,才选择它们,并在 int 边界处理 OverflowException。