Skip to content

Conversation

pangeorg
Copy link
Contributor

Replaces e.g. "System.ArgumentException: '0' cannot be greater than -2." with
System.ArgumentException: 'minValue = 0' cannot be greater than maxValue = -2
This follows the behavior of System.Math.Random

Fix #115337

Replaces e.g. "System.ArgumentException: '0' cannot be greater than -2."
with
System.ArgumentException: 'minValue = 0' cannot be greater than maxValue
= -2
This follows the behavior of System.Math.Random

Fix dotnet#115337
@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Aug 17, 2025
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Aug 17, 2025
@jkotas jkotas added area-System.Numerics and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Aug 17, 2025
Copy link
Contributor

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

@xtqqczze
Copy link
Contributor

Should the same changes be made in BigInteger.Clamp?

static void ThrowMinMaxException<T>(T min, T max)
{
throw new ArgumentException(SR.Format(SR.Argument_MinMaxValue, min, max));
}

xtqqczze added a commit to xtqqczze/dotnet-runtime that referenced this pull request Aug 18, 2025
Use Min/Max for value clamping

References: dotnet#118824 (comment)
xtqqczze added a commit to xtqqczze/dotnet-runtime that referenced this pull request Aug 18, 2025
Use Min/Max for value clamping

References: dotnet#118824 (comment)
pangeorg and others added 2 commits August 19, 2025 07:02
* Apply the Math.cs change to BigInteger.Clamp
* Use 'nameof'
@kasperk81
Copy link
Contributor

better replace ThrowMinMaxException with

private static void ThrowLess<T>(T value, T other, string? paramName) =>

pangeorg and others added 11 commits August 19, 2025 09:20
Co-authored-by: kasperk81 <83082615+kasperk81@users.noreply.github.com>
* Use ArgumentOutOfRangeException.ThrowLess in Math.cs and BigInteger.cs
This is to support throwing the error from INumber.Clamp as
ArgumentOutOfRangeException.ThrowIfLessThan has a type constraint
IComparable<T> so it cannot be used with nullable types.
All other places use ArgumentOutOfRangeException.ThrowIfLessThan
directly.
Assert that Math.Clamp throws ArgumentOutOfRangeException
@jeffhandley
Copy link
Member

@tannergooding I'm going to merge this improvement to the exception message and I'll file a new issue recommending we relax Math.Clamp to have the logic you described instead of throwing.

@jeffhandley jeffhandley merged commit 1d56b2a into dotnet:main Sep 2, 2025
142 of 144 checks passed
@xtqqczze
Copy link
Contributor

xtqqczze commented Sep 2, 2025

Looks like there are large regressions in code quality for floating point types, see: https://csharp.godbolt.org/z/K4P4xvWdK.

@tannergooding
Copy link
Member

That'd be because it uses IComparable which is not the same as x < y due to having to handle and normalize NaN

@xtqqczze
Copy link
Contributor

xtqqczze commented Sep 2, 2025

That'd be because it uses IComparable which is not the same as x < y due to having to handle and normalize NaN

Yeah, this PR also changes behaviour in relation to NaN, see sharplab.

@jkotas
Copy link
Member

jkotas commented Sep 2, 2025

It sounds like that this PR should be reverted?

@xtqqczze
Copy link
Contributor

xtqqczze commented Sep 2, 2025

It sounds like that this PR should be reverted?

Yeah, Math.Clamp now has different behaviour to INumber.Clamp:

static virtual TSelf Clamp(TSelf value, TSelf min, TSelf max)
{
if (min > max)
{
Math.ThrowMinMaxException(min, max);
}
TSelf result = value;
result = TSelf.Max(result, min);
result = TSelf.Min(result, max);
return result;
}

@xtqqczze
Copy link
Contributor

xtqqczze commented Sep 2, 2025

That'd be because it uses IComparable which is not the same as x < y due to having to handle and normalize NaN

@tannergooding It is quite unintuitive that ThrowIfLessThan behaves as if it was a hypothetical ThrowIfBeforeWhenSorted. If this cannot be changed, it should be documented.

@tannergooding
Copy link
Member

It is quite unintuitive that ThrowIfLessThan behaves as if it was a hypothetical

It's that there are multiple types of relational comparisons, just like there are multiple types of "equality" (Equals vs == can differ). This is just a standard part of .NET and many different ecosystems.

You have different considerations based on what the spec may require and what may be required for sorting or for use in hash sets, etc. You may need to even customize this which is why we also define https://learn.microsoft.com/en-us/dotnet/api/system.numerics.totalorderieee754comparer-1?view=net-9.0.

There is no single right answer and its not something we can document absolutely everywhere that takes IComparable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-System.Numerics community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Math.Clamp ArgumentException uses SR.Argument_MinMaxValue differently than Random.Next()
7 participants