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

11

126

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

Ranitta answered 21/4, 2010 at 13:47 Comment(10)
@Danvil: There is no "C# Class Library". You mean "The .NET Framework".Kellyekellyn
Still nothing as of C# 7.1?Vestryman
@JohnSaunders I don't believe that's strictly true #808380Simons
@Bob Some words have historical, well defined meaning. Clamp is one of them. en.wikipedia.org/wiki/Clamping_(graphics) or khronos.org/registry/OpenGL-Refpages/gl4/html/clamp.xhtml or learn.microsoft.com/en-us/windows/win32/direct3dhlsl/… "Limit" would be misleading, especially that "limit" already has a different meaning in maths.Rori
It is now added to .NET 5.0Melodic
@Bob I have only ever heard and used "clamp" in 25 years as a professional software engineer. Have never used "limit". This is while working in the UK, the USA and Canada.Gelhar
@Bob I'm fairly confident that programmers are allowed to use languages outside of .NET. Every professional C++ codebase I've worked with in my career has had a "clamp" function defined somewhere, usually as a template func if hand-rolled, or just using the std::clamp function in recent years. en.cppreference.com/w/cpp/algorithm/clamp There is no std::limit, needless to say.Gelhar
To recap, your claim was: "If I asked how to "limit" a value every single English speaking programmer in the world would know immediately what I meant." Honestly, I wouldn't know specifically what you meant, because I've never heard the term used. I could guess at what the speaker meant, and indeed if forced to speculate prior to this conversation I would think they might be imposing a max or a min on it. But if they said "clamp", I would know immediately: essentially a combo min and max operation to constrain the value within a specified range.Gelhar
Well, IT CLEARLY ISN'T OBSCURE THEN, is it? You're basically speaking like a neophyte to software engineering who is surprised and annoyed that a term that is so widespread as to have become a keyword in several major languages does not accord with his everyday, non-SE thought on what the word ought to be. "only gaining traction relatively recently" Sure, if 1998 counts as 'recently'. "So I think you've made my point for me." O, the irony. Welcome to the world of computer programming, btw.Gelhar
Bob, Really hope you're enjoying this late transition to being a programmer. Best of luck! CSS: css-tricks.com/snippets/sass/clamping-number C#: tutorialkart.com/c-sharp-tutorial/c-sharp-math-clamp Java: demo2s.com/java/java-math-clamp-int-val-int-min-int-max.html Rust: docs.rs/num/0.2.1/num/fn.clamp.html C++: geeksforgeeks.org/stdclamp-in-cpp-17 Boost: geeksforgeeks.org/boostalgorithmclamp-in-c-library Ruby: jemma.dev/blog/comparable-clampGelhar
M
176

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);
Moyra answered 21/4, 2010 at 13:53 Comment(13)
Where would I put this and is calling CompareTo slower than comparing with < (for integral types)?Ranitta
In a static class, and in the .NET framework (not sure about mono, compact, etc.), the generic should be recompiled for the type, and CompareTo inlined, so no performance penalty.Barbados
@Frasier Unless this is ultra performance sensitive code, you are unlikely to be making any meaningful performance gains by doing so. Having it be generic is probably more useful than saving a few microseconds.Pensive
The good thing about constraining to the generic version of IComparable is that no boxing occurs. This ought to run very fast. Remember that with double and float, the CompareTo method corresponds to a total order where NaN is less than all other values, including NegativeInfinity. So it is not equivalent to the < operator. If you used < with a floating-point type, you would have to consider how to treat NaN also. This is not relevant for other numeric types.Supervisor
You would need to consider how to treat NaN in either case. The version with < and > would output NaN and using NaN for min or max would effectively make a one-sided clamp. With CompareTo it would always return NaN if max is NaN.Macro
Nice solution Lee. An equally helpful extension method for some things, in the same vein: public static T TrapInvalid<T>(this T val, T min, T max, T valueIfInvalid) where T : IComparable<T> { if (val.CompareTo(min) < 0) return valueIfInvalid; else if (val.CompareTo(max) > 0) return valueIfInvalid; else return val; }Polygamist
A static function would be better as the extension suggest that the method is changing the type itself. But it is not!Raven
@CodeClown - Extension methods are static methods so you can call it as int c = Clamp(a, b, c) if you want. However I don't see how this implementation suggests the type is being changed. If you mean it implies the this parameter is being mutated, then I disagree since it returns a value.Moyra
@Moyra - See example Having no extension would prevent the error as foo itself is never clamped. Would the extension have a more verbose name like CalcClampedVal this would be obvious. But now that I think about it more, my issue could be considered minor.Raven
@CodeClown - I see the issue although I think your example will result in a warning about an ignored return value. The benefit of using an extension method is that it makes the clamped argument explicit.Moyra
I'd throw in a 'struct' constraint on that generic T type, so we don't accidentally use it on reference types and risk null exceptions, unless you do want to use it on reference types, of course. Those two 'else' statements are also redundant.Comestible
It's quite normal for methods to return a transformed version of the argument; they do necessarily mutate the object. A common convention is to use an appropriate verb tense, e.g., list.Sort to sort in place and list.Sorted to return a sorted copy of list. By that convention, this should be called Clamped rather than Clamp.Laundes
Two more remarks: (1) You do not constrain to value types, so if T is a reference type and val is null, we get a NullReferenceException. Note that Comparer<T>.Default.Compare(val, min) will consider null less than non-null, so you could use that if you wanted. Or check for null. Or constrain with where T : struct. (2) If some idiot one day passes a min and max that are in the wrong relative order, then it (unintensionally) becomes important that you check with min first. (Since val could satisfy both conditions.) We should throw an exception instead in that case.Supervisor
M
38

Just use Math.Min and Math.Max:

x = Math.Min(Math.Max(x, a), b);
Marni answered 7/12, 2013 at 15:31 Comment(4)
That translates to int a0 = x > a ? x : a; return a0 < b ? a0 : b which (although gives correct results) isn't exactly ideal.Mcgrew
@Marni If we know that min <= max, Math.Min(Math.Max(x, min), max) results in one more comparison than necessary if x < min.Laundes
@JimBalter, in theory this is true. If you look at how CompareTo() is typically implemented, the accepted answer can take upto 6 comparisons. I don't know though, if the compiler is smart enough and inlines the CompareTo() and removes the superfluous comparisons.Stull
This is good for cases when you only need to do it once, then whole new function for that feels like an overkill.Collectanea
H
25

Try:

public static int Clamp(int value, int min, int max)  
{  
    return (value < min) ? min : (value > max) ? max : value;  
}
Heathendom answered 14/6, 2010 at 20:14 Comment(3)
Ugh! Those ugly redundant parenthesis! If you're going to be an evil genius with the double ternary operators, at least do it properly and get rid of those as well! 😂Thomism
@XenoRo Those "redundant" parenthesis are what makes it readable.Inadvertent
@Cleaner - 1) If you're going for readability, double ternaries would be avoided and IF blocks would be used instead. 2) You don't get the joke, do you? xDThomism
H
13

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
Hock answered 21/4, 2010 at 13:51 Comment(3)
This solution is better than the accepted one. No ambiguity.Raven
@CodeClown This solution results in an unnecessary comparison when value > max, and the inverted argument order invites (and virtually guarantees) bugs. I don't know what ambiguity you think is avoided.Laundes
For consistency with the legacy Math.Clamp implementation, recommend switching the order of the min/max parameters: Clamp(T value, T min, T max)Herald
A
10

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:

Arezzini answered 21/4, 2010 at 13:51 Comment(1)
Now there is: Math.ClampRarotonga
A
6

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
Accession answered 29/1, 2021 at 15:13 Comment(0)
T
4

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

Thomism answered 2/10, 2017 at 21:18 Comment(1)
Another limitation is nameof does not work for C# 5 or below.Section
G
0

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;
    }
}
Grania answered 11/12, 2017 at 21:58 Comment(1)
Why not return value.ClampedMinimum(min).ClampedMaximum(max);?Zizith
S
0

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);
    }
}
Scorpio answered 28/3, 2019 at 23:35 Comment(0)
M
0

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);
Medico answered 24/2, 2020 at 14:5 Comment(0)
L
0

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;
    }
  }
}
Lexicon answered 3/5, 2022 at 1:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.