在计算两个度量单位之间的比例时,我遇到了一个精确的问题。
比率存储在UnitOfMeasureTable中的SQL Server数据库中: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
花了一段时间才弄清楚你必须将除数乘以1米才能得到正确的结果。如果你将sellUom乘以1米,它甚至更加怪异。 然后结果是:
RatioRight = (sellUom * 1m) / (buyUom)
RatioRight 0.000453
我猜这与SQL Server如何存储Decimal(28,18)以及Linq如何转换divide命令有关。
更新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
答案 0 :(得分:3)
这绝对是由于SQL Server如何处理精度和计算中的比例。
如果更改@mikeymouses示例以对@ p1使用等级6,则会得到一致的结果:
DECLARE @p2 Decimal(28,6) = 1000000
DECLARE @p3 Decimal(28,18) = 453.59237
select @p3 / @p2
DECLARE @p1 Decimal(19,18) = 1 -- This is how Linq sends the * 1m
select @p3 / (@p2 *@p1)
select (@p3 *@p1) / @p2
结果:
0.0004535923700000000000
0.00045359237000
0.00045359237000000000
精确度和量表结果记录在https://stackoverflow.com/a/38150585/3410584上,但关键点是: 对于乘法:
对于分部:
其中p1和p2是操作数和s1的精度,s2是操作数的比例。
应该记住,结果精度和标度的绝对最大值为38.当结果精度大于38时,相应的标度会减小,以防止结果的整数部分被截断。 我想这就是这里发生的事情。
答案 1 :(得分:1)
看起来这就是SQL如何处理这些声明的十进制大小除以彼此
DECLARE @p2 Decimal(28,18) = 1000000
DECLARE @p3 Decimal(28,18) = 453.59237
select @p3 / @p2
DECLARE @p1 Decimal(19,18) = 1 -- This is how Linq sends the * 1m
select @p3 / (@p2 *@p1)
select (@p3 *@p1) / @p2
结果:
0.0004535923
0.000453
0.00045359