如何将"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)
来获取内部表示。这对于比较两个值是否 相同 很有用,有关更多详细信息,您可以查阅文档以了解每个位的含义。
我有一个数据库 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)
来获取内部表示。这对于比较两个值是否 相同 很有用,有关更多详细信息,您可以查阅文档以了解每个位的含义。