Skip to content

Commit 9597b34

Browse files
lipchevangularsen
andauthored
Refactored the clamp function (preserving the unit) (#1608)
- refactored the clamp function: no longer converting using the boxing `ToUnit(Enum)` and no longer comparing with the `CompareTo(object)` - updated the tests --------- Co-authored-by: Andreas Gullberg Larsen <[email protected]>
1 parent 5d65646 commit 9597b34

File tree

2 files changed

+107
-73
lines changed

2 files changed

+107
-73
lines changed

UnitsNet.Tests/UnitMathTests.cs

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -156,26 +156,40 @@ public void SumOfLengthsWithSelectorCalculatesCorrectly()
156156
}
157157

158158
[Fact]
159-
public void ClampCalculatesCorrectly()
159+
public void Clamp_ReturnsValue_WhenWithinBounds()
160160
{
161161
var min = Length.FromMeters(-1);
162162
var max = Length.FromCentimeters(150);
163-
164-
var value1 = Length.FromMillimeters(33);
165-
166-
Length clampedValue = UnitMath.Clamp(value1, min, max);
163+
var value = Length.FromMillimeters(33);
164+
165+
Length clampedValue = UnitMath.Clamp(value, min, max);
166+
167167
Assert.Equal(33, clampedValue.Value);
168168
Assert.Equal(LengthUnit.Millimeter, clampedValue.Unit);
169+
}
169170

170-
var value2 = Length.FromMillimeters(-1500);
171-
172-
Length clampedMin = UnitMath.Clamp(value2, min, max);
171+
[Fact]
172+
public void Clamp_ReturnsMinAsValueUnit_WhenValueIsBelowMin()
173+
{
174+
var min = Length.FromMeters(-1);
175+
var max = Length.FromCentimeters(150);
176+
var value = Length.FromMillimeters(-1500);
177+
178+
Length clampedMin = UnitMath.Clamp(value, min, max);
179+
173180
Assert.Equal(-1000, clampedMin.Value);
174181
Assert.Equal(LengthUnit.Millimeter, clampedMin.Unit);
182+
}
175183

176-
var value3 = Length.FromMillimeters(2000);
177-
178-
Length clampedMax = UnitMath.Clamp(value3, min, max);
184+
[Fact]
185+
public void Clamp_ReturnsMaxAsValueUnit_WhenValueIsAboveMax()
186+
{
187+
var min = Length.FromMeters(-1);
188+
var max = Length.FromCentimeters(150);
189+
var value = Length.FromMillimeters(2000);
190+
191+
Length clampedMax = UnitMath.Clamp(value, min, max);
192+
179193
Assert.Equal(1500, clampedMax.Value);
180194
Assert.Equal(LengthUnit.Millimeter, clampedMax.Unit);
181195
}

UnitsNet/UnitMath.cs

Lines changed: 82 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,96 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Numerics;
1+
namespace UnitsNet;
52

6-
namespace UnitsNet
3+
/// <summary>
4+
/// Extension methods for common math operations.
5+
/// </summary>
6+
public static class UnitMath
77
{
8+
/// <summary>Returns the smaller of two <typeparamref name="TQuantity" /> values.</summary>
9+
/// <typeparam name="TQuantity">The type of quantities to compare.</typeparam>
10+
/// <param name="val1">The first of two <typeparamref name="TQuantity" /> values to compare.</param>
11+
/// <param name="val2">The second of two <typeparamref name="TQuantity" /> values to compare.</param>
12+
/// <returns>Parameter <paramref name="val1" /> or <paramref name="val2" />, whichever is smaller.</returns>
13+
public static TQuantity Min<TQuantity>(TQuantity val1, TQuantity val2)
14+
where TQuantity : IQuantity, IComparable<TQuantity>
15+
{
16+
return val1.CompareTo(val2) == 1 ? val2 : val1;
17+
}
18+
19+
/// <summary>Returns the larger of two <typeparamref name="TQuantity" /> values.</summary>
20+
/// <typeparam name="TQuantity">The type of quantities to compare.</typeparam>
21+
/// <param name="val1">The first of two <typeparamref name="TQuantity" /> values to compare.</param>
22+
/// <param name="val2">The second of two <typeparamref name="TQuantity" /> values to compare.</param>
23+
/// <returns>Parameter <paramref name="val1" /> or <paramref name="val2" />, whichever is larger.</returns>
24+
public static TQuantity Max<TQuantity>(TQuantity val1, TQuantity val2)
25+
where TQuantity : IQuantity, IComparable<TQuantity>
26+
{
27+
return val1.CompareTo(val2) == -1 ? val2 : val1;
28+
}
29+
830
/// <summary>
9-
/// A set of extension methods for some of the most common Math operations, such as Min, Max, Sum and Average
31+
/// Clamps the specified <paramref name="value" /> to the inclusive range defined by <paramref name="min" /> and
32+
/// <paramref name="max" />.
1033
/// </summary>
11-
public static class UnitMath
34+
/// <typeparam name="TQuantity">
35+
/// The type of the quantity, which must implement <see cref="IQuantityOfType{TQuantity}" /> and
36+
/// <see cref="IComparable{TQuantity}" />.
37+
/// </typeparam>
38+
/// <param name="value">The value to clamp.</param>
39+
/// <param name="min">The minimum allowable value.</param>
40+
/// <param name="max">The maximum allowable value.</param>
41+
/// <returns>
42+
/// The clamped value:
43+
/// <list type="bullet">
44+
/// <item>
45+
/// <description>
46+
/// <paramref name="value" /> if it lies within the range [<paramref name="min" />,
47+
/// <paramref name="max" />].
48+
/// </description>
49+
/// </item>
50+
/// <item>
51+
/// <description>
52+
/// <paramref name="min" /> (converted to value.Unit) if <paramref name="value" /> is less than
53+
/// <paramref name="min" />.
54+
/// </description>
55+
/// </item>
56+
/// <item>
57+
/// <description>
58+
/// <paramref name="max" /> (converted to value.Unit) if <paramref name="value" /> is greater than
59+
/// <paramref name="max" />.
60+
/// </description>
61+
/// </item>
62+
/// </list>
63+
/// </returns>
64+
/// <exception cref="ArgumentException">
65+
/// Thrown if <paramref name="min" /> is greater than <paramref name="max" />.
66+
/// </exception>
67+
public static TQuantity Clamp<TQuantity>(TQuantity value, TQuantity min, TQuantity max)
68+
where TQuantity : IQuantityOfType<TQuantity>, IComparable<TQuantity>
1269
{
13-
/// <summary>Returns the smaller of two <typeparamref name="TQuantity" /> values.</summary>
14-
/// <typeparam name="TQuantity">The type of quantities to compare.</typeparam>
15-
/// <param name="val1">The first of two <typeparamref name="TQuantity" /> values to compare.</param>
16-
/// <param name="val2">The second of two <typeparamref name="TQuantity" /> values to compare.</param>
17-
/// <returns>Parameter <paramref name="val1" /> or <paramref name="val2" />, whichever is smaller.</returns>
18-
public static TQuantity Min<TQuantity>(TQuantity val1, TQuantity val2)
19-
where TQuantity : IQuantity, IComparable<TQuantity>
70+
UnitKey unitKey = value.UnitKey;
71+
#if NET
72+
TQuantity minValue = TQuantity.Create(min.As(unitKey), unitKey);
73+
TQuantity maxValue = TQuantity.Create(max.As(unitKey), unitKey);
74+
#else
75+
TQuantity minValue = value.QuantityInfo.Create(min.As(unitKey), unitKey);
76+
TQuantity maxValue = value.QuantityInfo.Create(max.As(unitKey), unitKey);
77+
#endif
78+
79+
if (minValue.CompareTo(maxValue) > 0)
2080
{
21-
return val1.CompareTo(val2) == 1 ? val2 : val1;
81+
throw new ArgumentException($"min ({min}) cannot be greater than max ({max})", nameof(min));
2282
}
2383

24-
/// <summary>Returns the larger of two <typeparamref name="TQuantity" /> values.</summary>
25-
/// <typeparam name="TQuantity">The type of quantities to compare.</typeparam>
26-
/// <param name="val1">The first of two <typeparamref name="TQuantity" /> values to compare.</param>
27-
/// <param name="val2">The second of two <typeparamref name="TQuantity" /> values to compare.</param>
28-
/// <returns>Parameter <paramref name="val1" /> or <paramref name="val2" />, whichever is larger.</returns>
29-
public static TQuantity Max<TQuantity>(TQuantity val1, TQuantity val2)
30-
where TQuantity : IQuantity, IComparable<TQuantity>
84+
if (value.CompareTo(minValue) < 0)
3185
{
32-
return val1.CompareTo(val2) == -1 ? val2 : val1;
86+
return minValue;
3387
}
34-
35-
/// <summary>Returns <paramref name="value" /> clamped to the inclusive range of <paramref name="min" /> and <paramref name="max" />.</summary>
36-
/// <param name="value">The value to be clamped.</param>
37-
/// <param name="min">The lower bound of the result.</param>
38-
/// <param name="max">The upper bound of the result.</param>
39-
/// <returns>
40-
/// <paramref name="value" /> if <paramref name="min" /> ≤ <paramref name="value" /> ≤ <paramref name="max" />.
41-
///
42-
/// -or-
43-
///
44-
/// <paramref name="min" /> (converted to value.Unit) if <paramref name="value" /> &lt; <paramref name="min" />.
45-
///
46-
/// -or-
47-
///
48-
/// <paramref name="max" /> (converted to value.Unit) if <paramref name="max" /> &lt; <paramref name="value" />.
49-
/// </returns>
50-
/// <exception cref="ArgumentException">
51-
/// <paramref name="min" /> cannot be greater than <paramref name="max" />.
52-
/// </exception>
53-
public static TQuantity Clamp<TQuantity>(TQuantity value, TQuantity min, TQuantity max) where TQuantity : IComparable, IQuantity
54-
{
55-
var minValue = (TQuantity)min.ToUnit(value.Unit);
56-
var maxValue = (TQuantity)max.ToUnit(value.Unit);
57-
58-
if (minValue.CompareTo(maxValue) > 0)
59-
{
60-
throw new ArgumentException($"min ({min}) cannot be greater than max ({max})", nameof(min));
61-
}
62-
63-
if (value.CompareTo(minValue) < 0)
64-
{
65-
return minValue;
66-
}
6788

68-
if (value.CompareTo(maxValue) > 0)
69-
{
70-
return maxValue;
71-
}
72-
73-
return value;
89+
if (value.CompareTo(maxValue) > 0)
90+
{
91+
return maxValue;
7492
}
93+
94+
return value;
7595
}
7696
}

0 commit comments

Comments
 (0)