如何将"downgrade" C# 小数数据类型转换为任意小数位?

How to "downgrade" C# Decimal data type to some arbitrary number of decimal places?

我有一个数据库 table,其中的一个字段定义为类似这样的非常简化的形式

CREATE TABLE Product
(
    ProductId INT,
    Price SMALLMONEY
)

我有一个 LINQ entity framework 查询,其简化形式类似于:

Iqueryable query = from Product in testDB.Products
        select new
        {
            Price = Product.Price
        }

问题是这会产生我的其他 post

中描述的意外和不需要的行为

我对此进行了研究,得出的结论是,这种不良行为是由于我的查询结果 returns 字段价格为带 4 位小数的小数。这是可以理解的,因为 SMALLMONEY 以 4 位小数精度存储在数据库中。 如果我减少小数位数,一切正常。在这个简单的例子中是这样的

Iqueryable query = from Product in testDB.Products
            select new
                {
                    Price = 1.12m;  // Decimal literal with 2 decimals
 }

所以我认为这个数字四舍五入到小数点后两位就可以了。

Iqueryable query = from Product in testDB.Products
                select new
                {
                    Price = Decimal.Round(Products.Price,2) // round to two decimals    
                }

错了。价格 Price 以某种方式保留了它存储 4 个小数点的知识,而我的 Web 表单一团糟。

我查看了在 http://referencesource.microsoft.com/#mscorlib/system/decimal.cs

找到的 DecimalRound() 的源代码
[System.Security.SecuritySafeCritical]  // auto-generated
public static Decimal Round(Decimal d, int decimals)
{
    FCallRound (ref d, decimals);
    return d;
}

这通过引用传递参数值,所以我猜只有参数值发生了变化,而不是描述精度的内部 "meta" 数据。

我能做什么?我怎样才能将精度降到两位小数。

在没有更多上下文的情况下,所有可以建议的是转换为不同的数据类型:

select new 
{
    Price = (double) Products.Price
};

或者一个字符串:

select new
{
    Price = Products.Price.ToString("##.00")
}

我怀疑问题是您实际上没有使用 decimal.Round 执行舍入。相反,您在被翻译成 SQL.

的查询中表达它

如果您希望在 .NET 中完成舍入,则需要确保您使用的是 LINQ to Objects - 最简单的方法是调用 AsEnumerable。例如:

var results = testDB.Products
    .Select(p => new { p.Name, p.Price })
    .AsEnumerable()
    .Select(p => new { p.Name, Price = decimal.Round(p.Price, 2) });

请注意,您应该确保在AsEnumerable()之前进行任何排序、过滤等操作,以避免拉下所有结果并在本地进行过滤。

附带说明一下,虽然没有简单的 属性 来获取比例、符号和有效数字,但您可以使用 decimal.GetBits(decimal) 来获取内部表示。这对于比较两个值是否 相同 很有用,有关更多详细信息,您可以查阅文档以了解每个位的含义。