Where can I find the “clamp” function in .NET?

前端 未结 9 1036
野的像风
野的像风 2020-11-27 04:24

I would like to clamp a value x to a range [a, b]:

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

This is quite basic

相关标签:
9条回答
  • 2020-11-27 04:43

    The below code supports specifying bounds in any order (i.e. bound1 <= bound2, or bound2 <= bound1). I've found this useful for clamping values calculated from linear equations (y=mx+b) where the slope of the line can be increasing or decreasing.

    I know: The code consists of five super-ugly conditional expression operators. The thing is, it works, and the tests below prove it. Feel free to add strictly unnecessary parentheses if you so desire.

    You can easily create other overloads for other numeric types and basically copy/paste the tests.

    Warning: Comparing floating point numbers is not simple. This code does not implement double comparisons robustly. Use a floating point comparison library to replace the uses of comparison operators.

    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 tests:

    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);
        }
    }
    
    0 讨论(0)
  • 2020-11-27 04:46

    Just sharing Lee's solution with the comments' issues and concerns addressed, where possible:

    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;
    }
    

    Differences:

    • Method name uses the appropriate verb tense (ed) to (further) indicate that the value is not clamped in-place, and that, instead, a new value is returned (See @JimBalter's comment).
    • Does appropriate null check on all inputs (See @JeppeStigNielsen's comment).
    • Swaps min and max if min > max (See @JeppeStigNielsen's comment).

    Limitations: No one-sided clamps. If max is NaN, always returns NaN (See Herman's comment).

    0 讨论(0)
  • 2020-11-27 04:50

    Just use Math.Min and Math.Max:

    x = Math.Min(Math.Max(x, a), b);
    
    0 讨论(0)
  • 2020-11-27 04:50

    Using the previous answers, I condensed it down to the below code for my needs. This will also allow you to clamp a number only by its min or max.

    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;
        }
    }
    
    0 讨论(0)
  • 2020-11-27 04:52

    You could write an extension method:

    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;
    }
    

    Extension methods go in static classes - since this is quite a low-level function, it should probably go in some core namespace in your project. You can then use the method in any code file that contains a using directive for the namespace e.g.

    using Core.ExtensionMethods
    
    int i = 4.Clamp(1, 3);
    

    .NET Core 2.0

    Starting with .NET Core 2.0 System.Math now has a Clamp method that can be used instead:

    using System;
    
    int i = Math.Clamp(4, 1, 3);
    
    0 讨论(0)
  • 2020-11-27 04:52

    Try:

    public static int Clamp(int value, int min, int max)  
    {  
        return (value < min) ? min : (value > max) ? max : value;  
    }
    
    0 讨论(0)
提交回复
热议问题