如何使Decimal.Round()抛出OverflowException异常

11

我正在使用

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);

有人知道什么样的输入会抛出这个异常吗?


5
当在.NET中除以两个Decimal数时出现Overflow异常。 - Equalsk
4
这里是.NET Core CLR中目标调用的实现,可以在此处查看(https://github.com/dotnet/coreclr/blob/32f0f9721afb584b4a14d69135bea7ddc129f755/src/classlibnative/bcltype/decimal.cpp#L175)。你可以在第188行看到`OverflowException`的抛出。 - James Thorpe
3
这似乎是VarDecRound函数的实现。乍一看,我无法理解为什么它会向FCallRound返回失败结果(导致引发异常)。它要么返回E_INVALIDARG,但该条件已被FCallRound检查过,要么返回NOERROR,因此似乎不应该引发OverflowException异常。 - James Thorpe
2
@Equalsk 刚刚运行了那个示例。它没有抛出 OverflowException 异常。我正在使用 FW4.5.2。 - Amir Katz
11
永远不会发生。自动化函数的规范不是很好,没有记录它可以返回哪种故障代码。因此CLR作者认为,如果出现故障,那么只能是由于溢出而导致。这是不可能的,Decimal.Min/MaxValue是整数值。这也可以从VarDecRound的Unix版本中看出:https://github.com/dotnet/coreclr/blob/master/src/palrt/decarith.cpp#L1215 - Hans Passant
8
在1990年代,我曾经和编写那个库的人挨着办公室,他们开玩笑地把文档(当时当然还是纸质的)称为“谎言之书”。那份文档确实不太好,而且显然在过去20多年里也没有人加以改善。 - Eric Lippert
1个回答

3
当我们深入研究你已经发现的内容时,我们会进入VarDecRound函数的实现。这个函数只有一个分支会返回错误代码,那就是当它的第二个参数cDecimals小于零时。这个参数指示要舍入的小数位数:
if (cDecimals < 0) 
    return E_INVALIDARG; 

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

正如James Thorpe在对OP的评论中指出的那样,一个类似的断言在调用链的更高层次上执行,在此处

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

结论:
执行无法到达引发OverflowException的那一点, 正如文档所述:

  1. OverflowException似乎在内部被用作捕获所有异常的机制,就像GDI+中的OutOfMemoryException一样
  2. 文档与实际实现不符
  3. OverflowException在概念上甚至没有意义。将一个值四舍五入到相同的数据类型中,不可能超过整数最小或最大范围,因为候选值本身必须在范围内(使用的舍入方法)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接