46

In C#, I often have to limit an integer value to a range of values. For example, if an application expects a percentage, an integer from a user input must not be less than zero or more than one hundred. Another example: if there are five web pages which are accessed through Request.Params["p"], I expect a value from 1 to 5, not 0 or 256 or 99999.

I often end by writing a quite ugly code like:

page = Math.Max(0, Math.Min(2, page));

or even uglier:

percentage =
    (inputPercentage < 0 || inputPercentage > 100) ?
    0 :
    inputPercentage;

Isn't there a smarter way to do such things within .NET Framework?

I know I can write a general method int LimitToRange(int value, int inclusiveMinimum, int inlusiveMaximum) and use it in every project, but maybe there is already a magic method in the framework?

If I need to do it manually, what would be the "best" (ie. less uglier and more fast) way to do what I'm doing in the first example? Something like this?

public int LimitToRange(int value, int inclusiveMinimum, int inlusiveMaximum)
{
    if (value >= inclusiveMinimum)
    {
        if (value <= inlusiveMaximum)
        {
            return value;
        }

        return inlusiveMaximum;
    }

    return inclusiveMinimum;
}
Arseni Mourzenko
  • 50,338
  • 35
  • 112
  • 199
  • 9
    It's usually called 'clamp': see http://stackoverflow.com/questions/516500/is-there-a-canonical-name-for-a-function-combining-min-and-max and http://stackoverflow.com/questions/427477/fastest-way-to-clamp-a-real-fixed-floating-point-value and http://stackoverflow.com/questions/2683442/where-can-i-find-the-clamp-function-in-net and so on. – ShreevatsaR Jul 04 '10 at 23:55

8 Answers8

73

This operation is called 'Clamp' and it's usually written like this:

public static int Clamp( int value, int min, int max )
{
    return (value < min) ? min : (value > max) ? max : value;
}
Trap
  • 12,050
  • 15
  • 55
  • 67
  • 3
    I like the name Clamp. Even the Microsoft.Xna.Framework has a Clamp method :) – Syd Jul 05 '10 at 02:26
  • 3
    Clamp is the normal name. See the OpenCL specification. – IamIC Mar 12 '13 at 00:58
  • 8
    Ps. This code is more efficient with <= and >=, rather than < and >. :-) – IamIC Mar 12 '13 at 00:58
  • 2
    Add the 'this' and you can use it as an extension method: public static int Clamp(this int value, int min, int max ) { return (value < min) ? min : (value > max) ? max : value; } Now you can say: myInt.Clamp(10,50); – Molecool May 17 '18 at 21:21
  • 2
    Starting from .NET Standard 2.1 (Core 2.0) there is built in [Math.Clamp()](https://learn.microsoft.com/en-us/dotnet/api/system.math.clamp) method. – Kuba Szostak Jan 29 '22 at 15:47
36

A much cleaner method that will work with more than just integers (taken from my own library of shared code):

public static T Clamp<T>(T value, T min, T max) where T : IComparable<T>
{
    if (value.CompareTo(min) < 0)
        return min;
    if (value.CompareTo(max) > 0)
        return max;

    return value;
}
dtb
  • 213,145
  • 36
  • 401
  • 431
MikeP
  • 7,829
  • 33
  • 34
  • 12
    That throws a NullReferenceException if `value` is `null`. Better use an IComparer (e.g. `Comparer.Default`) instead. http://msdn.microsoft.com/en-us/library/azhsac5f.aspx – dtb Jul 05 '10 at 00:14
  • 1
    As of C#8 you can add notnull after IComparable to avoid the exception pointed out by dtb. `public static T Clamp(T value, T min, T max) where T : IComparable, notnull { // your choice of clamp method body }` – Jacob Huckins Jun 02 '20 at 23:10
  • This is brilliant and very elegant, because it does cover any comparable type (e.g. DateTime) and not just numbers! – David Liebeherr Jan 25 '22 at 23:27
  • 1
    You could add a check to ensure that min is not greater than max. – David Liebeherr Jan 25 '22 at 23:30
  • Here is a slightly improved version: https://pastebin.com/0N0PaG6a – David Liebeherr Jan 25 '22 at 23:42
32

I see Mark's answer and raise it by a this:

public static class InputExtensions
{
    public static int LimitToRange(
        this int value, int inclusiveMinimum, int inclusiveMaximum)
    {
        if (value < inclusiveMinimum) { return inclusiveMinimum; }
        if (value > inclusiveMaximum) { return inclusiveMaximum; }
        return value;
    }
}

Usage:

int userInput = ...;

int result = userInput.LimitToRange(1, 5)

See: Extension Methods

dtb
  • 213,145
  • 36
  • 401
  • 431
  • 4
    Please note that using extension methods does not have to be necessarily a good thing. It's just another programming tool and it has its uses. Put it in the wrong place or context and you'll end up writing unreadable code. – Trap Jul 05 '10 at 21:18
  • 1
    Your answer should really be just a comment suggesting improvements to his answer. – MMalke Oct 05 '18 at 12:31
17

I like Guffa's answer, but I am surprised that no one has yet posted a solution using Min/Max.

public int LimitInclusive(int value, int min, int max)
{
    return Math.Min(max, Math.Max(value, min));
}
Ed S.
  • 122,712
  • 22
  • 185
  • 265
  • Maybe using `Min` and `Max` each time would have a performance impact? – Arseni Mourzenko Jul 05 '10 at 00:12
  • Because this only supports integers and types that can be implicitly cast into integers – Orestis P. Jul 10 '20 at 06:16
  • Thanks for posting this. It is indeed much faster on my system and should be used if you can deal with working with int data types. I believe this is branchless and will use the math optimized instructions on the processor. – John Evans Jul 22 '20 at 12:07
  • 1
    @OrestisP.: Which is what the OP needed. Not every function your write needs to be able to handle any use case you can think of. That's a great way to waste a bunch of time building an over-engineered mess. – Ed S. Jul 23 '20 at 17:36
  • In all actuality, `Math.Max` (and `Math.Min` also) has 10 overloads; it can handle integers (`int`, `long`, `uint`, etc.) and floating-point (`float`, `double`) as well. So if OP needs to handle floating-point as well, they can overload `LimitInclusive`. – pepoluan May 15 '21 at 16:20
  • @Ed S. Agreed, fortunately what you said does not apply in this case. – Orestis P. Jul 07 '22 at 00:02
8

An alternative way to write your LimitToRange function is as follows.

public int LimitToRange(int value, int inclusiveMinimum, int inclusiveMaximum)
{
    if (value < inclusiveMinimum) { return inclusiveMinimum; }
    if (value > inclusiveMaximum) { return inclusiveMaximum; }
    return value;
}

I think this is a little easier to understand while still being efficient.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • 3
    @MainMa: If the value is in range, you have to perform two checks, no matter in which order you put your statements. Performance-wise all answers should be equal. – dtb Jul 05 '10 at 00:18
  • 2
    Using <= and >= can avoid the second check or a jump on edge cases :-) – IamIC Mar 12 '13 at 01:01
3

No, there isn't any method for that built in the framework. I suppose that it was left out because you already have Min and Max, so you can accomplish it using them.

If you write your own method for it, it doesn't matter much how you write it. If you use if statements or the conditional operator ?, it will still compile to pretty much the same code anyway.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • 3
    This sounds like an argument for ceasing all efforts in new and/or updating programming languages if we "already have [the tools]" !! ... we had the tools to write our own Min Max functions so why did they give us those?? – noelicus Aug 18 '16 at 13:05
3

To clamp values without giving users any feedback that the value entered by them is wrong, in general, might not be a great idea (IMHO). This might lead to subtle bugs later, which are difficult to debug, especially when min/max values are determined at run time.

Think of this. You have $100 in your bank account, and you want to transfer $150 to your friend. Would you like your banking system to throw an InsufficientFundsException or get into a discussion with your friend that you transferred $150 but he received only $100 (assuming the bank clamped the transfer amount to 100 since your did not have sufficient funds)

That being said, you should also look at code contracts.

public void MyFunction (Type input)
{
   Contract.Requires(input > SomeReferenceValue);
   Contract.Requires (input < SomeOtherReferencValue);

}

This will force the user input to be within the range.

ram
  • 11,468
  • 16
  • 63
  • 89
1

I like the Clamp name. I would suggest the following class

public class MathHelper
{
    public static int Clamp (int value,int min,int max)
    {
          // todo - implementation 
    }
    public static float Clamp (float value,float min,float max)
    {
          // todo - implementation 
    }
)

or if you want to use generics, then

public class MathHelper
{
     public static T Clamp<T> (T value, T min, T max) where T : IComparable
     {
         // todo - implementation
         T output = value;
         if (value.CompareTo(max) > 0)
         {
             return max;
         }
         if (value.CompareTo(min) < 0)
         {
            return min;
         }
        return  output;
     } 
}
Lucius
  • 2,794
  • 4
  • 20
  • 42
Syd
  • 1,526
  • 1
  • 15
  • 16