Skip to content

[release/11.0] Make DegreesToRadians and RadiansToDegrees correctly rounded - #132578

Merged
tannergooding merged 1 commit into
release/11.0from
backport/pr-132096-to-release/11.0
Aug 21, 2026
Merged

[release/11.0] Make DegreesToRadians and RadiansToDegrees correctly rounded#132578
tannergooding merged 1 commit into
release/11.0from
backport/pr-132096-to-release/11.0

Conversation

@github-actions

@github-actionsgithub-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Backport of #132096 to release/11.0

/cc @tannergooding

Customer Impact

  • Customer reported
  • Found internally

Customers can receive infinity when converting a finite float or double value if the intermediate multiplication overflows, even though the correctly rounded result is finite. For non-overflowing inputs, the existing implementations are also frequently 1 ULP inaccurate and can be 2 ULP inaccurate. This was reported in #131930.

Regression

  • Yes
  • No

This corrects the original implementation of these APIs rather than a regression introduced in the current release.

Testing

The implementation was validated against exact rational arithmetic: exhaustively for Half, BFloat16, float (all 2^31 finite positive inputs), and Decimal32 (60 million inputs). The more complex double implementation was validated using mathematically derived rounding boundaries, including 372,000 enumerated near-tie vectors and the complete subnormal band. Decimal64 and Decimal128 were validated against the same near-tie and subnormal coverage. All cases were correctly rounded.

Unit tests cover scalar floating-point and decimal conversions, including special values, overflow boundaries, subnormals, and known near-tie cases. The vector implementations use the same validated algorithm.

Risk

Low. Every possible input was exhaustively validated for the smaller binary formats, and Decimal32 was exhaustively validated across 60 million inputs. The more complex double path was validated against exact rational arithmetic using mathematically derived boundary and near-tie cases plus the complete subnormal range. The decimal implementations use exact integer arithmetic, and the vector paths reuse the corresponding validated scalar algorithm.

Note

This PR description was drafted by GitHub Copilot on behalf of the author.

`x * (pi / 180)` rounds twice -- once forming the constant, once forming
the product -- leaving up to ~1 ulp of error. Over all `2^31` finite
positive `float` inputs the current shape misrounds 37.9% of them, and
0.042% are off by 2 ulp.
Carry the factor as a multi-limb value and fold the limbs back with a
single rounding. Two limbs suffice for all but a rare input, and whether
they did is itself decidable, so the third limb is only paid for when
needed; a scaled path covers the subnormals.
Covers `float`, `double`, `Half`, `BFloat16`, `Vector64/128/256/512`,
`Vector<T>`, and `Decimal32/64/128`.
Fixes#131930
----------
Validated against exact rational arithmetic: exhaustively for `Half`,
`BFloat16`, `float` (all `2^31`) and `Decimal32` (60M); for `double` and
the larger decimals against an enumerated near-tie set (372k vectors)
plus replay of the whole subnormal band. 0 misrounded in every case.
----------
Cost in ns per operation, over a 4096 element `double` array storing to
a destination, so a `Vector256` entry is one call handling four
elements. Ad hoc harness on a 7950X, and both directions measure the
same within noise.
| | before | after |
|---|---|---|
| scalar | 0.90 | 1.02 |
| `Vector128` | 0.88 | 1.81 |
| `Vector256` | 0.85 | 1.92 |
| `Vector512` | 1.65 | 3.00 |
The old code was bound by `vdivsd` / `vdivpd` throughput rather than by
the multiply, which is why it cost about the same at every width. The
new code has no divide and is FP issue bound instead, so it scales with
the work rather than with the divider. `Vector512` is double pumped on
this part, hence roughly twice `Vector256` in both columns.
Inputs below the point where the two limb form stops working take a
scaled path, at roughly 13 ns.
----------
- The vector paths use `Vector*.FusedMultiplyAdd` where the hardware has
it and a Veltkamp split otherwise, since recovering the roundoff of a
product requires a real fused multiply-add.
- Decimal zero now carries the IEEE preferred exponent:
`Decimal32.DegreesToRadians(0E0)` is `0E-17`, was `0E-8`.
- The six new `Decimal*` members mirror the existing `double` / `float`
`ITrigonometricFunctions` surface.
> [!NOTE]
> Portions of this pull request were drafted with Copilot.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

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

@jeffhandleyjeffhandley left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has my support for .NET 11 RC2

@artl93artl93 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We Should Do Math Right. Approved.

@tannergooding

Copy link
Copy Markdown
Member

/ba-g httplistener timeouts

@tannergooding
tannergooding merged commit e08a61f into release/11.0Aug 21, 2026
132 of 139 checks passed
@tannergooding
tannergooding deleted the backport/pr-132096-to-release/11.0 branch August 21, 2026 04:42
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-rc2 milestone Aug 21, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.NumericsServicing-approvedApproved for servicing release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@tannergooding@jeffhandley@artl93