Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Floating-point to integer conversions should saturate on overflow #101254

Closed
tannergooding opened this issue Apr 18, 2024 · 2 comments
Closed

Floating-point to integer conversions should saturate on overflow #101254

tannergooding opened this issue Apr 18, 2024 · 2 comments

Comments

@tannergooding
Copy link
Member

tannergooding commented Apr 18, 2024

Current Behavior

The result of converting from a floating-point type to an integral type when the floating-point value is not representable in the target integer type is currently considered "unspecified". The Roslyn compiler currently normalizes this to be 0 across all platforms and architectures for determinism purposes (dotnet/roslyn#37772).

References

This is as per the language spec (https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/conversions.md):

  • For a conversion from float or double to an integral type, the processing depends on the overflow-checking context (§12.8.19) in which the conversion takes place:
  • In a checked context, the conversion proceeds as follows:
    • If the value of the operand is NaN or infinite, a System.OverflowException is thrown.
    • Otherwise, the source operand is rounded towards zero to the nearest integral value. If this integral value is within the range of the destination type then this value is the result of the conversion.
    • Otherwise, a System.OverflowException is thrown.
  • In an unchecked context, the conversion always succeeds, and proceeds as follows.
    • If the value of the operand is NaN or infinite, the result of the conversion is an unspecified value of the destination type.
    • Otherwise, the source operand is rounded towards zero to the nearest integral value. If this integral value is within the range of the destination type then this value is the result of the conversion.
    • Otherwise, the result of the conversion is an unspecified value of the destination type.

The runtime spec (ECMA-335) -- III.3.27 conv.:

If overflow occurs converting a floating-point type to an integer, or if the floating-point value being converted to an integer is a NaN, the value returned is unspecified.

and the IEEE 754 (otherwise known as ISO/IEC 60559) specification (noting that for the IEEE 754 specification, there is allowed disabling of floating-point exceptions, which is how the runtime operates):

When a NaN or infinite operand cannot be represented in the destination format and this cannot otherwise be indicated, the invalid operation exception shall be signaled. When a numeric operand would convert to an integer outside the range of the destination format, the invalid operation exception shall be signaled if this situation cannot otherwise be indicated.

Problem

While the behavior is left up to the implementation by all these specifications, this has lead to problems for users that are targeting different platforms (x64 vs Arm64) and is incompatible with some targets that require a more stringent behavior (WASM).

Accordingly, the runtime has made steps towards normalizing the behavior itself (#61885) and the behavior we landed on is not the same one Roslyn opted for. In particular, we do want NaN -> 0, but for overflow we want it to instead saturate (that is if the source is greater than the maximum value of the target type, it saturates to the maximum value of the target type; if it is less than the minimum, it saturates to the minimum. Thus float.MaxValue -> int.MaxValue, float.MinValue -> int.MinValue).

This decision was made based on the choices made by several other languages (such as Rust and Java), the behavior of more modern platforms (such as Arm64), and the required behavior for key platforms coming online (such as WASM).

Recommended Change

Accordingly, it would be beneficial if the Roslyn compiler could move towards the same behavior so that on .NET 9+ users will see the same results across all target CPU architectures regardless of whether the operation is constant folded by Roslyn, constant folded by the JIT/AOT, or evaluated at runtime.

This would simply entail that NaN -> 0 and overflow is done via saturation, as described above. If appropriate or helpful, the runtime can define a RuntimeFeature flag to help drive this behavior and indicate that the target is expected to saturate.

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label Apr 18, 2024
Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-numerics
See info in area-owners.md if you want to be subscribed.

@tannergooding
Copy link
Member Author

Meant to open this in dotnet/roslyn....

dotnet/roslyn#73084

@dotnet-policy-service dotnet-policy-service bot removed the untriaged New issue has not been triaged by the area owner label Apr 18, 2024
@github-actions github-actions bot locked and limited conversation to collaborators May 19, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

1 participant