값 x
을 범위 로 고정하고 싶습니다 [a, b]
.
x = (x < a) ? a : ((x > b) ? b : x);
이것은 아주 기본적인 것입니다. 하지만 클래스 라이브러리에서 "클램프"함수를 볼 수 없습니다 System.Math
. 적어도 .
( "클램핑"하는 것을 모르는 사람에게는 값이 최대 값과 최소값 사이에 있는지 확인하는 것입니다. 최대 값보다 크면 최대 값 등으로 대체됩니다.)
값 x
을 범위 로 고정하고 싶습니다 [a, b]
.
x = (x < a) ? a : ((x > b) ? b : x);
이것은 아주 기본적인 것입니다. 하지만 클래스 라이브러리에서 "클램프"함수를 볼 수 없습니다 System.Math
. 적어도 .
( "클램핑"하는 것을 모르는 사람에게는 값이 최대 값과 최소값 사이에 있는지 확인하는 것입니다. 최대 값보다 크면 최대 값 등으로 대체됩니다.)
답변:
확장 메서드를 작성할 수 있습니다.
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
두 경우 모두 치료 방법을 고려해야합니다 . <
and >
would가 있는 버전은 for 또는 이를 NaN
사용하여 효과적으로 단면 클램프를 만듭니다. 로 그것을 항상 반환 하는 경우 IS . NaN
min
max
CompareTo
NaN
max
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 int Clamp(int value, int min, int max)
{
return (value < min) ? min : (value > max) ? max : value;
}
하나는 없지만 만드는 것이 너무 어렵지 않습니다. 여기에서 하나를 찾았습니다. 클램프
그것은:
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
네임 스페이스 에는 하나도 없습니다 .
XNA 게임 스튜디오MathHelper
에서 사용할 수 있는 클래스가 있습니다 .
가능한 경우 해결 된 의견의 문제 및 우려 사항과 함께 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
이며 NaN
, 항상 반환 NaN
(참조 허먼의 주석 ).
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
선의 기울기가 증가하거나 감소 할 수있는 선형 방정식 ( )에서 계산 된 값을 클램핑하는 데 유용함을 발견했습니다 .
알아요 : 코드는 5 개의 매우 못생긴 조건식 연산자로 구성 됩니다. 문제는 작동합니다 하며 아래 테스트가이를 증명합니다. 원하는 경우 엄격하게 불필요한 괄호를 자유롭게 추가하십시오.
다른 숫자 유형에 대해 다른 오버로드를 쉽게 만들고 기본적으로 테스트를 복사 / 붙여 넣기 할 수 있습니다.
경고 : 부동 소수점 수를 비교하는 것은 간단하지 않습니다. 이 코드는 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);