【发布时间】:2017-01-11 09:18:45
【问题描述】:
我在计算两个度量单位之间的比率时遇到了一个精度问题。
比率存储在 SQL Server 数据库中的 UnitOfMeasureTable 中:NumberOfDefaultUnits decimal(28,18)
在我的 linqPad 示例中,我有一个类来演示出了什么问题。
private class Test
{
public decimal saleUom { get; set; }
public decimal piUom { get; set; }
public decimal RatioRight { get; set; }
public decimal RatioWrong { get; set; }
public decimal CalcledAlsoRight { get { return saleUom / piUom; } }
}
void Main() {
var xx = from uom in UnitsOfMeasures.Where(d=> d.Id == 9)
let buyUom = uom.NumberOfDefaultUnits
let sellUom = UnitsOfMeasures.First(d=> d.Id == 13).NumberOfDefaultUnits
select new Test
{
saleUom = sellUom,
piUom = buyUom,
RatioRight = sellUom / (buyUom * 1m),
RatioWrong = sellUom / buyUom,
};
xx.First().Dump();
}
结果是:
saleUom 453.592370000000000000
piUom 1000000.000000000000000000
RatioRight 0.000453592370000000000
RatioWrong 0.0004535923
CalcledAlsoRight 0.00045359237
花了一些时间才发现必须将除数乘以 1m 才能得到正确的结果。如果将 sellUom 乘以 1m,情况会更奇怪。 那么结果是:
RatioRight = (sellUom * 1m) / (buyUom)
RatioRight 0.000453
我猜这与 SQL Server 如何存储 Decimal(28,18) 以及 Linq 如何转换除法命令有关。
更新 2: 看起来这完全是一个 SQL 舍入的事情。从等式中删除 .Net
select top 1 uom.NumberOfDefaultUnits
from UnitOfMeasures uom
where uom.Id = 13
select (select top 1 uom.NumberOfDefaultUnits
from UnitOfMeasures uom
where uom.Id = 13 )
/
(select top 1 uom.NumberOfDefaultUnits
from UnitOfMeasures uom
where uom.Id = 9)
第一个查询返回:453.592370000000000000
第二个:0.0004535923
【问题讨论】:
-
看起来
buyUom是integer。我敢打赌这与此有关。如果将它与1m相乘,它会转换为decimal。在这种情况下,我不确定小数与整数相除的效果是什么。 -
@HoneyBadger 不,buyUom 是十进制的。它直接来自 UnitOfMeasure 表中的 Decimal(28,18) 列。将添加截图
-
你能看看
buyUom的值是多少吗?它可能与分配给piUom时的值不同(相对于有效数字)。 -
@HoneyBadger piUom 的价值?在数据库中,它显示为“1000000.000000000000000000”。我不认为这是一个价值观问题。查看 CalcledAlsoRight 属性。它运行 100% 相同的东西“saleUom / piUom”它得到了正确的答案,但发送到 SQL 的版本得到了错误。
-
我认为这与重要数字有关。 .NET 中的小数有 28-29 个有效数字,在 sql 中,无论您将它们定义为多少,所以在您的情况下它将是 18。这就是为什么我希望
buyUom与piUom有不同数量的有效数字。当小数被划分时,计算得到的精度和比例。我不知道 .NET 是如何做到这一点的,但我相当有信心您的问题是由此引起的。当您乘以1m时,有效数字再次设置为 .NET 默认值 (28-29),这将改变结果的精度和比例。
标签: sql-server linq decimal rounding