【问题标题】:Overflow Exception when dividing two decimals in .NET在 .NET 中除以两位小数时出现溢出异常
【发布时间】:2010-07-08 13:16:30
【问题描述】:

我在尝试将两位小数相除然后显示结果时遇到问题。令人讨厌的是,这只发生在我们的服务器上,如果我在本地运行代码,它似乎工作得很好。这是我要运行的代码


decimal dOne = -966.96M;
decimal dTwo = 2300M;

decimal dResult = Decimal.Round((dOne / dTwo), 28, 
                               MidpointRounding.AwayFromZero);

结果数(从 windows 计算器生成)是

-0.43346086956521739130434782608696

这总是会导致溢出异常:

System.OverflowException: Value was either too large or too small for a Decimal.
   at System.Decimal.FCallDivide(Decimal& result, Decimal d1, Decimal d2)
   at System.Decimal.op_Division(Decimal d1, Decimal d2)

这确实有点道理,因为结果数字超过 32 位小数,而小数点最多只能保留 28 位。但我不确定如何执行此除法,因为它似乎正在存储结果在内存中的十进制类型中,然后将其四舍五入并存储它。我也尝试将其直接转换为字符串,而不是将其存储在十进制中,但这有同样的问题。

有什么想法吗?我是否做了一些明显愚蠢的事情(很可能),有没有更好的方法来执行这个计算?

【问题讨论】:

  • 难道你有一个 32 位服务器,但一个 64 位工作站?
  • 如果只是,它是一个基于 64 位 xeon 的服务器,运行服务器 2003 x64 和 .net 3.5 :(
  • 肯定要四舍五入到 28 位,您需要查看第 29 位。如果你要求 27,它还会这样做吗?

标签: c# .net decimal division


【解决方案1】:

在计算前尝试转换为double,如果需要,然后再转换回decimal

decimal dOne = -966.96M;
decimal dTwo = 2300M;

double one = (double)dOne;
double two = (double)dTwo;

double result = one / two;

decimal dResult = (decimal)result; // Additional rounding may be necessary

【讨论】:

  • 我将此标记为我的答案,因为在与我的同事交谈后,这仅在这种特定情况下发生,我们可以应对精度损失。已选择为溢出添加 try/catch 并转换为 double 如果它现在发生..尽管如果有更好的答案我宁愿在将来把它放进去:)
【解决方案2】:

应该运行良好。该除法不能保证返回 完全 准确的版本 - 例如,1 / 3m 可以正常工作。

结果显然不在decimal范围之外,所以在我看来,您的服务器上发生了一些奇怪的事情。

要检查的一件事:是Decimal.Round 引发了异常,还是除法本身?将它们放入单独的语句中以找出答案。

【讨论】:

  • 感谢 jon 的评论 - 我最初将它们放在不同的行上,并尝试使用和不使用 Decimal.Round,所以我很确定四舍五入不是问题。这绝对是奇怪的,因为完全相同的代码在沙盒和我的桌面上工作......令人沮丧的问题;)
【解决方案3】:

如果它发生在您的服务器上(您无法调试的地方)。您真的确定问题出在这几行之内吗?

也许您可以只在单个 Decimal.Round 语句周围放置一个 try-catch 语句,并返回一些奇怪的值。您能否再次在您的服务器上运行此代码,以查看此 catch 语句是否真的被调用,或者异常是否可能发生在其他地方。

【讨论】:

    【解决方案4】:

    我通过 Reflector 查看了Decimal.Round,据我所知,它不会抛出OverflowException,所以我打赌异常来自部门。您可以编辑答案以包含堆栈跟踪吗?

    另外,你确定分子和分母和你写的完全一样吗?尝试在异常发生时将它们跟踪到控制台或日志文件。

    你可以这样做:

    decimal dOne = -966.96M; 
    decimal dTwo = 2300M;  
    try
    {
      decimal dResult = Decimal.Round((dOne / dTwo), 28, MidpointRounding.AwayFromZero); 
    }
    catch (OverflowException)
    {
      Console.WriteLine(dOne);
      Console.WriteLine(dTwo);
    }
    

    编辑:我想我在 SSCLI 中找到了FCallDivide 的代码。但是,在 .NET Framework 的发布版本中可能会有所不同,但我可以从 SSCLI 中的完成方式看到,无论如何都会以多种不同的方式生成溢出异常。代码相当复杂。如果您可以构建一个简短但完整的程序来演示该问题,我会将其作为错误提交给 Microsoft。这些输入中可能存在某种使算法混乱的位模式。

    【讨论】:

    • 我添加了更多信息 - 之后它返回到其他不相关的代码。我添加了一些日志记录以提取除法之前的数字,而这些就是它正在使用的数字。目前,我正在查看 Tomas 发布的双重转换解决方案,因为我在这里并不需要超过 10/12 DP 左右的精度,而且看起来它会起作用..但需要进行更多测试。
    • 好吧,这显然发生在部门内部。这很奇怪。您在本地和服务器上使用什么版本的框架?我会尝试考虑其他解决此问题的方法,但这会很困难。我会看看我是否找不到FCallDivide 方法的代码。
    猜你喜欢
    • 2015-08-19
    • 2011-09-30
    • 1970-01-01
    • 1970-01-01
    • 2022-01-02
    相关资源
    最近更新 更多