Linq to SQL舍入后的十进制小数

时间:2017-01-11 09:18:45

标签: sql-server linq decimal rounding

在计算两个度量单位之间的比例时,我遇到了一个精确的问题。

比率存储在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命令有关。

更新:所有值均为小数 enter image description here

更新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

2 个答案:

答案 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 + 1
  • 结果比例= s1 + s2

对于分部:

  • 结果精度= p1 - s1 + s2 + max(6,s1 + p2 + 1)
  • 结果比例= max(6,s1 + p2 + 1)

其中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