Add BigMul across the integer types - #594
Merged
Merged
Conversation
Ten members: int.BigMul, uint.BigMul, long.BigMul and ulong.BigMul (net9), Int128.BigMul, UInt128.BigMul, nint.BigMul and nuint.BigMul (net11), and the two Math.BigMul out-overloads, which are net5 rather than netcoreapp3.0 as the docs imply, and were missing on every target below that. The three net9 Math.BigMul overloads cannot be added. Math.BigMul(uint, uint), Math.BigMul(long, long) and Math.BigMul(ulong, ulong) have signatures identical to the numeric-type members above, and C# emits both static extension members onto the same class, so declaring both is CS0111. The numeric-type set is the coherent one to keep, and the omission is noted on each of the three. The four overloads returning Int128 or UInt128 are net7.0 and later, since those types do not exist below that. Noted, since the api_list section header does not show it. Verified against net11 by reconstructing every product from the returned halves and comparing to BigInteger: Math.BigMul(long, long, out long) returns a signed upper half over an unsigned lower half, and nint.BigMul splits at 32 or 64 bits depending on IntPtr.Size. The tests use the same oracle and run on every target, so they validate the BCL on net9.0 and later and the polyfill below. API count 1095 -> 1105.
This was referenced Sep 10, 2026
This was referenced Sep 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ten members. The audit item said "net9, 8 members"; the real surface is wider and one part of it turned out not to be polyfillable at all.
Math.BigMul(long, long, out long),Math.BigMul(ulong, ulong, out ulong)int.BigMul,uint.BigMul,long.BigMul,ulong.BigMulInt128.BigMul,UInt128.BigMul,nint.BigMul,nuint.BigMulThe two
Mathout-overloads are net5, not netcoreapp3.0The docs list these as .NET Core 3.0. The 3.0.0 and 3.1.0 reference assemblies expose only
Math.BigMul(int, int)— the 64-bit out-overloads first appear in 5.0.0. I had guarded onNETCOREAPP3_0_OR_GREATERand the netcoreapp3.0/3.1 Split builds failed, which is how it surfaced. So these were missing on every target below net5.0, not just below netcoreapp3.0, and are now polyfilled there with the limb-splitting fallback the runtime itself uses when there is no widening-multiply intrinsic.Three
Mathoverloads cannot be polyfilledMath.BigMul(uint, uint),Math.BigMul(long, long)andMath.BigMul(ulong, ulong)have signatures identical touint.BigMul,long.BigMulandulong.BigMul. C# emits static extension members onto the enclosing class with no receiver in the signature, soextension(Math)andextension(uint)declaring the same(uint, uint) → ulongis CS0111 — "Type 'Polyfill' already defines a member called 'BigMul' with the same parameter types".Only one of each pair can ship. I kept the numeric-type members: that yields a complete, consistent set of four (
int/uint/long/ulong), where theMathside would have been an incoherent mix —int.BigMulhas noMathtwin to collide with, so it would have shipped either way. Each of the three affected members carries a//Note:pointing at the omission.net7.0 floor on the 128-bit results
long.BigMulandulong.BigMulreturnInt128/UInt128, which do not exist below net7.0 and which Polyfill does not recreate. Those two, andInt128.BigMul/UInt128.BigMul, are therefore net7.0+. That is a clean floor rather than a hole in the middle of the range, but it is not visible from the#### Int64section header, so it is noted.Verified rather than assumed
Every case is reconstructed from the returned halves and compared against a
BigIntegerproduct:Math.BigMul(long, long, out long)returns a signed upper half over an unsigned lower half — mixing those up is silently wrong only for negative operands.nint.BigMulsplits at 32 or 64 bits depending onIntPtr.Size, so the upper half is not simply a sign extension on a 32-bit process.Int128.BigMulapplies the signed correctionupper - ((left >> 127) & right) - ((right >> 127) & left)to the unsigned 256-bit product.The tests use the same
BigIntegeroracle over an edge-case matrix (0, ±1, ±2,MinValue,MaxValue, alternating bit patterns) and run on every target framework, so on net9.0+ they validate the BCL and below it the polyfill.Result
API count 1095 → 1105.
Solution clean in Release, Consume clean across all 22 TFMs, tests green on net11.0 (1690), net10.0 (1690), net9.0 (1690), net8.0 (1687), net462 (1652), plus PublicTests, EmbeddedTests, UnsafeTests, NoRefsTests and NoExtrasTests. net7.0 and net5.0 compile but could not be run here — those runtimes are not installed on this machine — though net8.0 exercises the same
NET7_0_OR_GREATER && !NET9_0_OR_GREATERbranch that carries theInt128/UInt128polyfills, and net462 exercises the softwareMath.BigMulpath.