Как Decimal.Round() может вызывать OverflowException

я использую

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

Кто-нибудь понял, какой ввод может вызвать это исключение?


person Amir Katz    schedule 20.02.2017    source источник
comment
stackoverflow.com/ вопросы/3203959/   -  person Equalsk    schedule 20.02.2017
comment
FWIW, здесь целевой вызов реализован в .NET Core CLR — вы можете увидеть бросок OverflowException в строке 188.   -  person James Thorpe    schedule 20.02.2017
comment
И похоже, что это реализация VarDecRound. На первый взгляд, я не понимаю, почему он возвращает результат сбоя в FCallRound (что приводит к возникновению исключения). Он либо возвращает E_INVALIDARG, но условие для этого уже проверено FCallRound, либо NOERROR, поэтому кажется, что OverflowException на самом деле никогда не должно быть выброшено.   -  person James Thorpe    schedule 20.02.2017
comment
@Equalsk только что запустил этот пример. Он не вызывает OverflowException. Я использую FW4.5.2   -  person Amir Katz    schedule 20.02.2017
comment
Этого никогда не случится. Спецификация функции автоматизации не очень хороша, она не документирует, какие коды ошибок она может возвращать. Так что авторы CLR просто предполагают, что если она и не работает, то это может быть только из-за переполнения. Не произойдет, Decimal.Min/MaxValue являются целыми значениями. Также видно из версии VarDecRound для Unix: github.com /dotnet/coreclr/blob/master/src/palrt/   -  person Hans Passant    schedule 20.02.2017
comment
@HansPassant: В 1990-х у меня был офис рядом с парнями, которые написали эту библиотеку, и они в шутку называли документацию (которая тогда была на бумаге) книгой лжи. Эта документация действительно была не очень хорошей, и, по-видимому, никто не улучшал ее за последние 20 с лишним лет.   -  person Eric Lippert    schedule 20.02.2017


Ответы (1)


Когда мы идем дальше от того, что вы уже обнаружили сами, мы приходим к реализации VarDecRound. Эта функция имеет ровно одну ветвь, в которой она возвращает код ошибки, и это когда ее второй аргумент cDecimals меньше нуля. Этот аргумент указывает количество десятичных цифр для округления:

if (cDecimals < 0) 
    return E_INVALIDARG; 

(это утверждение эквивалентно тому, что ArgumentException было бы в .NET)

Как отметил Джеймс Торп в комментарии к OP, аналогичное утверждение делается дальше по цепочке вызовов, .cpp#L184" rel="nofollow noreferrer">здесь:

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

Вывод:
выполнение не может достичь точки это приведет к выдаче OverflowException, как задокументировано:

  1. OverflowException, по-видимому, использовался внутренне как универсальный механизм, очень похожий на OutOfMemoryException в GDI+.
  2. Документация не соответствует реальной реализации
  3. OverflowException не имеет смысла даже концептуально. Округление значения в большую или меньшую сторону в одном и том же типе данных не может превышать интегральный минимальный или максимальный диапазон, так как само значение-кандидат должно находиться в диапазоне (используемый метод округления)
person Cee McSharpface    schedule 15.12.2017