110

我想将一个值限制x在一个范围内[a, b]

x = (x < a) ? a : ((x > b) ? b : x);

这是非常基本的。但我在类库中没有看到函数“clamp”——至少在System.Math.

(对于不知道“钳制”一个值的人来说,要确保它位于一些最大值和最小值之间。如果它大于最大值,则将其替换为最大值等)

4

10 回答 10

158

你可以写一个扩展方法:

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 核心 2.0

从 .NET Core 2.0 开始,System.Math现在有一个Clamp可以替代使用的方法:

using System;

int i = Math.Clamp(4, 1, 3);
于 2010-04-21T13:53:10.507 回答
36

只需使用Math.MinMath.Max

x = Math.Min(Math.Max(x, a), b);
于 2013-12-07T15:31:01.627 回答
26

尝试:

public static int Clamp(int value, int min, int max)  
{  
    return (value < min) ? min : (value > max) ? max : value;  
}
于 2010-06-14T20:14:25.563 回答
13

没有,但制作一个并不难。我在这里找到了一个:夹子

这是:

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
于 2010-04-21T13:51:47.220 回答
10

System.Math命名空间中没有一个。

如果您正在做的事情恰好是XNA 游戏工作室可以使用的一个MathHelper类:

于 2010-04-21T13:51:15.207 回答
4

尽可能与评论的问题和关注点分享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;
}

差异:

限制:没有单侧夹具。如果maxNaN,总是返回NaN(参见Herman 的评论)。

于 2017-10-02T21:18:20.530 回答
0

使用以前的答案,我将其浓缩为以下代码以满足我的需要。这也将允许您仅通过最小值或最大值来限制数字。

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;
    }
}
于 2017-12-11T21:58:27.763 回答
0

下面的代码支持以任何顺序(即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);
    }
}
于 2019-03-28T23:35:23.987 回答
0

如果我想验证 [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);
于 2020-02-24T14:05:33.277 回答
0
System.Math.Clamp

如果您使用的是 .NET 5、.NET Core 2.x、3.x...

于 2021-01-29T15:13:28.407 回答