【问题标题】:How can Decimal.Round() throw OverflowExceptionDecimal.Round() 如何抛出 OverflowException
【发布时间】:2017-02-20 15:41:18
【问题描述】:

我正在使用

Decimal.Round(decimal d)

MSDN 说可以抛出OverflowException https://msdn.microsoft.com/en-us/library/k4e2bye2(v=vs.110).aspx

我不确定这是怎么发生的。我尝试使用 ilSpy 查看实现 直到外部实现:

// decimal
[SecurityCritical]
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void FCallRound(ref decimal d, int decimals);

有人知道什么输入会引发此异常吗?

【问题讨论】:

  • FWIW,this is where 目标调用是在 .NET Core CLR 中实现的 - 你可以在第 188 行看到 OverflowException 的抛出。
  • this appearsVarDecRound 的实现。乍一看,我不明白为什么它会向FCallRound 返回失败结果(导致抛出异常)。它要么返回E_INVALIDARG,但其条件已经被FCallRoundNOERROR 检查过,因此似乎 OverflowException 实际上不应该被抛出。
  • 这永远不会发生。自动化功能的规范不是很好,它没有记录它可以返回什么样的故障代码。所以 CLR 作者只是假设,如果它失败了,那么它可能只是因为溢出。不会发生,Decimal.Min/MaxValue 是整数值。也可以从 Unix 版本的 VarDecRound 中看到:github.com/dotnet/coreclr/blob/master/src/palrt/…
  • @HansPassant:在 1990 年代,我的办公室就在写图书馆的人旁边,他们开玩笑地将文档(当时当然是纸上的)称为“谎言之书” .该文档确实不是很好,并且在过去 20 多年中显然没有人对其进行改进。

标签: c# decimal coreclr overflowexception


【解决方案1】:

当我们从您已经发现的内容中走得更远时,我们最终会实现VarDecRound 函数。这个函数只有一个返回错误代码的分支,即当它的第二个参数cDecimals 小于零时。此参数表示要四舍五入的小数位数:

if (cDecimals < 0) 
    return E_INVALIDARG; 

(这种断言相当于 .NET 中的 ArgumentException

正如 James Thorpe 在对 OP 的评论中指出的那样,在调用链的上游也做了类似的断言,here

if (decimals < 0 || decimals > 28) 
    FCThrowArgumentOutOfRangeVoid(...)

结论:
执行无法到达the point,这将导致抛出OverflowException,如记录所示:

  1. OverflowException 似乎在内部被用作包罗万象的机制,很像 GDI+ 中的 OutOfMemoryException
  2. 文档与实际实现不符
  3. OverflowException 在概念上甚至没有意义。 在同一数据类型中向上或向下舍入一个值不可能超过 integral min or max range,因为候选值本身必须在范围内 (rounding method used)

【讨论】:

    猜你喜欢
    • 2011-10-03
    • 1970-01-01
    • 1970-01-01
    • 2017-08-28
    • 1970-01-01
    • 1970-01-01
    • 2012-06-26
    • 1970-01-01
    • 2014-12-23
    相关资源
    最近更新 更多