我想将值限制x
在一个范围内[a, b]
:
x = (x < a) ? a : ((x > b) ? b : x);
这是很基本的。但是我没有在类库中看到函数“ clamp”-至少没有System.Math
。
(对于不知道“钳位”的值是要确保它介于某个最大值和最小值之间。如果该值大于最大值,则将其替换为最大值,等等。)
我想将值限制x
在一个范围内[a, b]
:
x = (x < a) ? a : ((x > b) ? b : x);
这是很基本的。但是我没有在类库中看到函数“ clamp”-至少没有System.Math
。
(对于不知道“钳位”的值是要确保它介于某个最大值和最小值之间。如果该值大于最大值,则将其替换为最大值,等等。)
Answers:
您可以编写一个扩展方法:
public static T Clamp<T>(this T val, T min, T max) where T : IComparable<T>
{
if (val.CompareTo(min) < 0) return min;
else if(val.CompareTo(max) > 0) return max;
else return val;
}
编辑:扩展方法进入静态类-由于这是一个低级功能,它可能应该进入项目的某些核心名称空间。然后,您可以在包含名称空间的using指令的任何代码文件中使用该方法,例如
using Core.ExtensionMethods
int i = 4.Clamp(1, 3);
从.NET Core 2.0开始,System.Math
现在有一种Clamp
方法可以代替使用:
using System;
int i = Math.Clamp(4, 1, 3);
IComparable
是不会发生装箱。这应该运行得非常快。请记住,使用double
和时float
,该CompareTo
方法所对应的总阶次NaN
小于所有其他值,包括NegativeInfinity
。因此它不等同于<
运算符。如果使用<
浮点类型,则必须考虑如何处理NaN
。这与其他数字类型无关。
NaN
两种情况下的治疗方法。带有<
和的版本>
可以输出NaN
和使用NaN
,min
或者max
可以有效地制作一个单面夹。有了CompareTo
它总是返回NaN
如果max
IS NaN
。
只需使用Math.Min
和Math.Max
:
x = Math.Min(Math.Max(x, a), b);
int a0 = x > a ? x : a; return a0 < b ? a0 : b
哪一个(尽管给出正确的结果)并不完全理想。
Math.Min(Math.Max(x, min), max)
则如果x <min,则比必要的结果多一个。
没有一个,但是制作一个并不是很难。我在这里找到一个:钳
它是:
public static T Clamp<T>(T value, T max, T min)
where T : System.IComparable<T> {
T result = value;
if (value.CompareTo(max) > 0)
result = max;
if (value.CompareTo(min) < 0)
result = min;
return result;
}
它可以像这样使用:
int i = Clamp(12, 10, 0); -> i == 10
double d = Clamp(4.5, 10.0, 0.0); -> d == 4.5
Clamp(T value, T min, T max)
System.Math命名空间中没有一个
http://msdn.microsoft.com/zh-CN/library/system.math_members.aspx
如果碰巧是您正在做的事情,则可以在XNA游戏工作室中使用MathHelper类:
http://msdn.microsoft.com/zh-cn/library/bb197892(v=XNAGameStudio.31).aspx
仅在可能的情况下,将Lee的解决方案与评论的问题和关注共享:
public static T Clamped<T>(this T value, T min, T max) where T : IComparable<T> {
if (value == null) throw new ArgumentNullException(nameof(value), "is null.");
if (min == null) throw new ArgumentNullException(nameof(min), "is null.");
if (max == null) throw new ArgumentNullException(nameof(max), "is null.");
//If min <= max, clamp
if (min.CompareTo(max) <= 0) return value.CompareTo(min) < 0 ? min : value.CompareTo(max) > 0 ? max : value;
//If min > max, clamp on swapped min and max
return value.CompareTo(max) < 0 ? max : value.CompareTo(min) > 0 ? min : value;
}
差异:
ed
)来(进一步)指示该值未就位,而是返回一个新值(请参阅@JimBalter的注释)。null check
所有输入(请参阅@JeppeStigNielsen的注释)。min
和max
if min > max
(请参阅@JeppeStigNielsen的评论)。局限性:无单侧夹钳。如果max
为is NaN
,则始终返回NaN
(请参阅Herman的注释)。
nameof
不适用于C#5或更低版本。
使用前面的答案,我将其压缩为以下代码以满足我的需求。这也将允许您仅按数字的最小值或最大值来钳位一个数字。
public static class IComparableExtensions
{
public static T Clamped<T>(this T value, T min, T max)
where T : IComparable<T>
{
return value.CompareTo(min) < 0 ? min : value.ClampedMaximum(max);
}
public static T ClampedMinimum<T>(this T value, T min)
where T : IComparable<T>
{
return value.CompareTo(min) < 0 ? min : value;
}
public static T ClampedMaximum<T>(this T value, T max)
where T : IComparable<T>
{
return value.CompareTo(max) > 0 ? max : value;
}
}
return value.ClampedMinimum(min).ClampedMaximum(max);
呢?
以下代码支持以任何顺序(即bound1 <= bound2
或bound2 <= bound1
)指定范围。我发现这对于钳制由线性方程式(y=mx+b
)计算的值很有用,在线性方程式中,线的斜率可以增大或减小。
我知道:该代码由五个非常丑陋的条件表达式运算符组成。事实是,它起作用了,下面的测试证明了这一点。如果您愿意,可以随意添加严格不必要的括号。
您可以轻松地为其他数字类型创建其他重载,并基本上复制/粘贴测试。
警告:比较浮点数并不简单。此代码不能double
可靠地实现比较。使用浮点比较库来替换比较运算符。
public static class MathExtensions
{
public static double Clamp(this double value, double bound1, double bound2)
{
return bound1 <= bound2 ? value <= bound1 ? bound1 : value >= bound2 ? bound2 : value : value <= bound2 ? bound2 : value >= bound1 ? bound1 : value;
}
}
xUnit / FluentAssertions测试:
public class MathExtensionsTests
{
[Theory]
[InlineData(0, 0, 0, 0)]
[InlineData(0, 0, 2, 0)]
[InlineData(-1, 0, 2, 0)]
[InlineData(1, 0, 2, 1)]
[InlineData(2, 0, 2, 2)]
[InlineData(3, 0, 2, 2)]
[InlineData(0, 2, 0, 0)]
[InlineData(-1, 2, 0, 0)]
[InlineData(1, 2, 0, 1)]
[InlineData(2, 2, 0, 2)]
[InlineData(3, 2, 0, 2)]
public void MustClamp(double value, double bound1, double bound2, double expectedValue)
{
value.Clamp(bound1, bound2).Should().Be(expectedValue);
}
}
如果要验证[min,max]中参数的范围,则使用以下方便的类:
public class RangeLimit<T> where T : IComparable<T>
{
public T Min { get; }
public T Max { get; }
public RangeLimit(T min, T max)
{
if (min.CompareTo(max) > 0)
throw new InvalidOperationException("invalid range");
Min = min;
Max = max;
}
public void Validate(T param)
{
if (param.CompareTo(Min) < 0 || param.CompareTo(Max) > 0)
throw new InvalidOperationException("invalid argument");
}
public T Clamp(T param) => param.CompareTo(Min) < 0 ? Min : param.CompareTo(Max) > 0 ? Max : param;
}
该类适用于的所有对象IComparable
。我创建一个具有一定范围的实例:
RangeLimit<int> range = new RangeLimit<int>(0, 100);
我要么验证一个论点
range.Validate(value);
或将参数限制在以下范围内:
var v = range.Validate(value);