Where can I find the "clamp" function in .NET?

C#Clamp

C# Problem Overview


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. But I do not see a function "clamp" in the class library - at least not in System.Math.

(For the unaware to "clamp" a value is to make sure that it lies between some maximum and minimum values. If it’s greater than the max value, then it’s replaced by the max, etc.)

C# Solutions


Solution 1 - C#

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

Solution 2 - C#

Just use Math.Min and Math.Max:

x = Math.Min(Math.Max(x, a), b);

Solution 3 - C#

Try:

public static int Clamp(int value, int min, int max)  
{  
    return (value < min) ? min : (value > max) ? max : value;  
}

Solution 4 - C#

There isn't one, but it's not too hard to make one. I found one here: clamp

It is:

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

And it can be used like:

int i = Clamp(12, 10, 0); -> i == 10
double d = Clamp(4.5, 10.0, 0.0); -> d == 4.5

Solution 5 - C#

There isn't one in the System.Math namespace.

There is a MathHelper Class where it is available for the XNA game studio if that happens to be what you are doing:

Solution 6 - C#

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:

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

Solution 7 - C#

System.Math.Clamp is the method you want if you are on .NET 5+, .NET Core 3.x, or .NET Core 2.x.

var a = Math.Clamp(5, 1, 10); // = 5
var b = Math.Clamp(-99, 1, 10); // = 1
var c = Math.Clamp(99, 1, 10); // = 10

Solution 8 - C#

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

Solution 9 - C#

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

Solution 10 - C#

If I want to validate the range of an argument in [min, max], the I use the following handy class:

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

The class works for all object which are IComparable. I create an instance with a certain range:

RangeLimit<int> range = new RangeLimit<int>(0, 100);

I an either validate an argument

range.Validate(value);

or clamp the argument to the range:

var v = range.Validate(value);

Solution 11 - C#

Based on the @JeremyB answer, with suggested corrections.

namespace App
{
  /// <summary>
  /// Miscellaneous utilities.
  /// </summary>
  public static class Util
  {
    /// <summary>
    /// Clamp a value to the inclusive range [min, max].
    /// </summary>
    /// <remarks>
    /// In newer versions of the .NET Framework, there is a System.Math.Clamp() method. 
    /// </remarks>
    /// <typeparam name="T">The type of value.</typeparam>
    /// <param name="value">The value to clamp.</param>
    /// <param name="min">The minimum value.</param>
    /// <param name="max">The maximum value.</param>
    /// <returns>The clamped value.</returns>
    public static T clamp<T>( T value, T min, T max ) where T : System.IComparable<T>
    {
      if ( value.CompareTo( max ) > 0 )
      {
        return max;
      }

      if ( value.CompareTo( min ) < 0 )
      {
        return min;
      }

      return value;
    }
  }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDanvilView Question on Stackoverflow
Solution 1 - C#LeeView Answer on Stackoverflow
Solution 2 - C#d7samuraiView Answer on Stackoverflow
Solution 3 - C#ClitView Answer on Stackoverflow
Solution 4 - C#Jeremy B.View Answer on Stackoverflow
Solution 5 - C#kemiller2002View Answer on Stackoverflow
Solution 6 - C#XenoRoView Answer on Stackoverflow
Solution 7 - C#juFoView Answer on Stackoverflow
Solution 8 - C#Bobby SpeirsView Answer on Stackoverflow
Solution 9 - C#NathanAldenSrView Answer on Stackoverflow
Solution 10 - C#Rabbid76View Answer on Stackoverflow
Solution 11 - C#Mike FinchView Answer on Stackoverflow