dotnet / docs

This repository contains .NET Documentation.
https://learn.microsoft.com/dotnet
Creative Commons Attribution 4.0 International
4.22k stars 5.87k forks source link

Decimal and checked arithmetic #42428

Open BillWagner opened 1 week ago

BillWagner commented 1 week ago

Type of issue

Missing information

Description

From anonymous feedback:

In the section "Operations affected by the overflow-checking context", it should be mentioned that decimal type arithmetic operations will result in System.OverflowExceptions if the result of that operation produces a magnitude outside of the bounds of the decimal type, regardless of whether or not that operation is occurring in a checked or unchecked block.

Page URL

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/checked-and-unchecked

Content source URL

https://github.com/dotnet/docs/blob/main/docs/csharp/language-reference/statements/checked-and-unchecked.md

Document Version Independent Id

39f8bc85-d1c0-787c-b9b5-c3b4a7925327

Article author

@BillWagner

Metadata

BillWagner commented 1 week ago

/cc @tannergooding Can you verify the correct behavior for decimal as it relates to checked and unchecked arithmetic.

tannergooding commented 1 week ago

Yes, checked and unchecked largely only apply to integer like types where there is a sensible overflow behavior. -- More specifically, the wraparound behavior where T.MaxValue + 1 becomes T.MinValue is sensible in a two's complement value because the while the represented value is not "correct" since it cannot fit in n-bits, the raw bits are representative of the lower n-bits of the full (n + 1)-bit result, so it allows it to be decomposed.

For types like decimal, float, double or Half where you have a more complex value being represented or a one's complement representation, then wraparound no longer becomes "sensible" and cannot be used to compute larger or more accurate results, so unchecked becomes unbeneficial.

For float, double, and Half you do, however, have sensible saturating values for PositiveInfinity and NegativeInfinity so there is still a way to detect overflow while still being more generally composable. For decimal, no such limits exist and saturating at MaxValue can lead to errors or confusion, so the types always throw.

Ultimately, the behavior of checked and unchecked is per type and sometimes per operation. Even for integers, things like unchecked(x / 0) will still always throw because there is no sensible behavior to be had in this case. Developers need to check the behavior for their given language, the type being used, and the operation being done for that type to understand how the checked and unchecked keywords will impact their code.