Tutorial :How to elegantly check if a number is within a range?



Question:

How can I do this elegantly with C# and .NET 3.5/4?

For example, a number can be between 1 and 100.

I know a simple if would suffice; but the keyword to this question is elegance. It's for my toy project not for production.

This questions wasn't about speed, but about code beauty. Stop talking about efficiency and such; remember you're preaching to the choir.


Solution:1

There are a lot of options:

int x = 30;  if (Enumerable.Range(1,100).Contains(x))      //true    if (x >= 1 && x <= 100)      //true  

Also, check out this SO post for regex options.


Solution:2

Do you mean?

if(number >= 1 && number <= 100)  

or

bool TestRange (int numberToCheck, int bottom, int top)  {    return (numberToCheck >= bottom && numberToCheck <= top);  }  


Solution:3

Just to add to the noise here, you could create an extension method:

public static bool IsWithin(this int value, int minimum, int maximum)  {      return value >= minimum && value <= maximum;  }  

Which would let you do something like...

int val = 15;    bool foo = val.IsWithin(5,20);  

That being said, this seems like a silly thing to do when the check itself is only one line.


Solution:4

As others said, use a simple if.

You should think about the ordering.

e.g

1 <= x && x <= 100  

is easier to read than

x >= 1 && x <= 100  


Solution:5

You can reduce the number of comparisons from two to one by using some math. The idea is that one of the two factors becomes negative if the number lies outside of the range and zero if the number is equal to one of the bounds:

If the bounds are inclusive:

(x - 1) * (100 - x) >= 0  

or

(x - min) * (max - x) >= 0  

If the bounds are exclusive:

(x - 1) * (100 - x) > 0  

or

(x - min) * (max - x) > 0  

However, in production code I would simply write 1 < x && x < 100, it is easier to understand.


Solution:6

With a bit of extension method abuse, we can get the following "elegant" solution:

using System;    namespace Elegant {      public class Range {          public int Lower { get; set; }          public int Upper { get; set; }      }        public static class Ext {          public static Range To(this int lower, int upper) {              return new Range { Lower = lower, Upper = upper };          }            public static bool In(this int n, Range r) {              return n >= r.Lower && n <= r.Upper;          }      }        class Program {          static void Main() {              int x = 55;              if (x.In(1.To(100)))                  Console.WriteLine("it's in range! elegantly!");          }      }  }  


Solution:7

I propose this:

public static bool IsWithin<T>(this T value, T minimum, T maximum) where T : IComparable<T> {      if (value.CompareTo(minimum) < 0)         return false;      if (value.CompareTo(maximum) > 0)         return false;      return true;  }  

Examples:

45.IsWithin(32, 89)  true  87.2.IsWithin(87.1, 87.15)  false  87.2.IsWithin(87.1, 87.25)  true  

and of course with variables:

myvalue.IsWithin(min, max)  

It's easy to read (close to human language) and works with any comparable type (integer, double, custom types...).

Having code easy to read is important because the developer will not waste "brain cycles" to understand it. In long coding sessions wasted brain cycles make developer tired earlier and prone to bug.


Solution:8

If this is incidental, a simple if is all you need. If this happens in many places, you might want to consider these two:

  • PostSharp. Decorate methods with attributes that 'inject' code into the method after compilation. I don't know for sure, but I can imagine it can be used for this.

Something like:

[Between("parameter", 0, 100)]  public void Foo(int parameter)  {  }  
  • Code contracts. Has the advantage that the constraints can be checked at compile time, by static verification of your code and the places that use your code.


Solution:9

if (value > 1 && value < 100)  {      // do work  }  else  {      // handle outside of range logic  }  


Solution:10

Using an && expression to join two comparisons is simply the most elegant way to do this. If you try using fancy extension methods and such, you run into the question of whether to include the upper bound, the lower bound, or both. Once you start adding additional variables or changing the extension names to indicate what is included, your code becomes longer and harder to read (for the vast majority of programmers). Furthermore, tools like Resharper will warn you if your comparison doesn't make sense (number > 100 && number < 1), which they won't do if you use a method ('i.IsBetween(100, 1)').

The only other comment I'd make is that if you're checking inputs with the intention to throw an exception, you should consider using code contracts:

Contract.Requires(number > 1 && number < 100)  

This is more elegant than if(...) throw new Exception(...), and you could even get compile-time warnings if someone tries to call your method without ensuring that the number is in bounds first.


Solution:11

If you want to write more code than a simple if, maybe you can: Create a Extension Method called IsBetween

public static class NumberExtensionMethods  {      public static bool IsBetween(this long value, long Min, long Max)      {          // return (value >= Min && value <= Max);          if (value >= Min && value <= Max) return true;          else return false;      }  }  

...

// Checks if this number is between 1 and 100.  long MyNumber = 99;  MessageBox.Show(MyNumber.IsBetween(1, 100).ToString());  

Addendum: it's worth noting that in practice you very rarely "just check for equality" (or <, >) in a codebase. (Other than in the most trivial situations.) Purely as an example, any game programmer would use categories something like the following in every project, as a basic matter. Note that in this example it (happens to be) using a function (Mathf.Approximately) which is built in to that environment; in practice you typically have to carefully develop your own concepts of what comparisons means for computer representations of real numbers, for the type of situation you are engineering. (Don't even mention that if you're doing something like, perhaps a controller, a PID controller or the like, the whole issue becomes central and very difficult, it becomes the nature of the project.) BY no means is the OP question here a trivial or unimportant question.

private bool FloatLessThan(float a, float b)      {      if ( Mathf.Approximately(a,b) ) return false;      if (a<b) return true;      return false;      }    private bool FloatLessThanZero(float a)      {      if ( Mathf.Approximately(a,0f) ) return false;      if (a<0f) return true;      return false;      }    private bool FloatLessThanOrEqualToZero(float a)      {      if ( Mathf.Approximately(a,0f) ) return true;      if (a<0f) return true;      return false;      }  


Solution:12

Cause all the other answer are not invented by me, here just my implementation:

public enum Range  {      /// <summary>      /// A range that contains all values greater than start and less than end.      /// </summary>      Open,      /// <summary>      /// A range that contains all values greater than or equal to start and less than or equal to end.      /// </summary>      Closed,      /// <summary>      /// A range that contains all values greater than or equal to start and less than end.      /// </summary>      OpenClosed,      /// <summary>      /// A range that contains all values greater than start and less than or equal to end.      /// </summary>      ClosedOpen  }    public static class RangeExtensions  {      /// <summary>      /// Checks if a value is within a range that contains all values greater than start and less than or equal to end.      /// </summary>      /// <param name="value">The value that should be checked.</param>      /// <param name="start">The first value of the range to be checked.</param>      /// <param name="end">The last value of the range to be checked.</param>      /// <returns><c>True</c> if the value is greater than start and less than or equal to end, otherwise <c>false</c>.</returns>      public static bool IsWithin<T>(this T value, T start, T end) where T : IComparable<T>      {          return IsWithin(value, start, end, Range.ClosedOpen);      }        /// <summary>      /// Checks if a value is within the given range.      /// </summary>      /// <param name="value">The value that should be checked.</param>      /// <param name="start">The first value of the range to be checked.</param>      /// <param name="end">The last value of the range to be checked.</param>      /// <param name="range">The kind of range that should be checked. Depending on the given kind of range the start end end value are either inclusive or exclusive.</param>      /// <returns><c>True</c> if the value is within the given range, otherwise <c>false</c>.</returns>      public static bool IsWithin<T>(this T value, T start, T end, Range range) where T : IComparable<T>      {          if (value == null)              throw new ArgumentNullException(nameof(value));            if (start == null)              throw new ArgumentNullException(nameof(start));            if (end == null)              throw new ArgumentNullException(nameof(end));            switch (range)          {              case Range.Open:                  return value.CompareTo(start) > 0                         && value.CompareTo(end) < 0;              case Range.Closed:                  return value.CompareTo(start) >= 0                         && value.CompareTo(end) <= 0;              case Range.OpenClosed:                  return value.CompareTo(start) > 0                         && value.CompareTo(end) <= 0;              case Range.ClosedOpen:                  return value.CompareTo(start) >= 0                         && value.CompareTo(end) < 0;              default:                  throw new ArgumentException($"Unknown parameter value {range}.", nameof(range));          }      }  }  

You can then use it like this:

var value = 5;  var start = 1;  var end = 10;    var result = value.IsWithin(start, end, Range.Closed);  


Solution:13

A new twist on an old favorite:

public bool IsWithinRange(int number, int topOfRange, int bottomOfRange, bool includeBoundaries) {      if (includeBoundaries)          return number <= topOfRange && number >= bottomOfRange;      return number < topOfRange && number > bottomOfRange;  }  


Solution:14

In C, if time efficiency is crucial and integer overflows will wrap, one could do if ((unsigned)(value-min) <= (max-min)) .... If 'max' and 'min' are independent variables, the extra subtraction for (max-min) will waste time, but if that expression can be precomputed at compile time, or if it can be computed once at run-time to test many numbers against the same range, the above expression may be computed efficiently even in the case where the value is within range (if a large fraction of values will be below the valid range, it may be faster to use if ((value >= min) && (value <= max)) ... because it will exit early if value is less than min).

Before using an implementation like that, though, benchmark one one's target machine. On some processors, the two-part expression may be faster in all cases since the two comparisons may be done independently whereas in the subtract-and-compare method the subtraction has to complete before the compare can execute.


Solution:15

How about something like this?

if (theNumber.isBetween(low, high, IntEx.Bounds.INCLUSIVE_INCLUSIVE))  {  }  

with the extension method as follows (tested):

public static class IntEx  {      public enum Bounds       {          INCLUSIVE_INCLUSIVE,           INCLUSIVE_EXCLUSIVE,           EXCLUSIVE_INCLUSIVE,           EXCLUSIVE_EXCLUSIVE      }        public static bool isBetween(this int theNumber, int low, int high, Bounds boundDef)      {          bool result;          switch (boundDef)          {              case Bounds.INCLUSIVE_INCLUSIVE:                  result = ((low <= theNumber) && (theNumber <= high));                  break;              case Bounds.INCLUSIVE_EXCLUSIVE:                  result = ((low <= theNumber) && (theNumber < high));                  break;              case Bounds.EXCLUSIVE_INCLUSIVE:                  result = ((low < theNumber) && (theNumber <= high));                  break;              case Bounds.EXCLUSIVE_EXCLUSIVE:                  result = ((low < theNumber) && (theNumber < high));                  break;              default:                  throw new System.ArgumentException("Invalid boundary definition argument");          }          return result;      }  }  


Solution:16

I would do a Range object, something like this:

public class Range<T> where T : IComparable  {      public T InferiorBoundary{get;private set;}      public T SuperiorBoundary{get;private set;}        public Range(T inferiorBoundary, T superiorBoundary)      {          InferiorBoundary = inferiorBoundary;          SuperiorBoundary = superiorBoundary;      }        public bool IsWithinBoundaries(T value){          return InferiorBoundary.CompareTo(value) > 0 && SuperiorBoundary.CompareTo(value) < 0;      }  }  

Then you use it this way:

Range<int> myRange = new Range<int>(1,999);  bool isWithinRange = myRange.IsWithinBoundaries(3);  

That way you can reuse it for another type.


Solution:17

static class ExtensionMethods  {      internal static bool IsBetween(this double number,double bound1, double bound2)      {          return Math.Min(bound1, bound2) <= number && number <= Math.Max(bound2, bound1);      }        internal static bool IsBetween(this int number, double bound1, double bound2)      {          return Math.Min(bound1, bound2) <= number && number <= Math.Max(bound2, bound1);      }  }  

Usage

double numberToBeChecked = 7;

var result = numberToBeChecked.IsBetween(100,122);

var result = 5.IsBetween(100,120);

var result = 8.0.IsBetween(1.2,9.6);


Solution:18

I'd go with the more simple version:

if(Enumerable.Range(1,100).Contains(intInQuestion)) { ...DoStuff; }  


Solution:19

I was looking for an elegant way to do it where the bounds might be switched (ie. not sure which order the values are in).

This will only work on newer versions of C# where the ?: exists

bool ValueWithinBounds(float val, float bounds1, float bounds2)  {      return bounds1 >= bounds2 ?        val <= bounds1 && val >= bounds2 :         val <= bounds2 && val >= bounds1;  }  

Obviously you could change the = signs in there for your purposes. Could get fancy with type casting too. I just needed a float return within bounds (or equal to)


Solution:20

When checking if a "Number" is in a range you have to be clear in what you mean, and what does two numbers are equal mean? In general you should wrap all floating point numbers in what is called a 'epsilon ball' this is done by picking some small value and saying if two values are this close they are the same thing.

    private double _epsilon = 10E-9;      /// <summary>      /// Checks if the distance between two doubles is within an epsilon.      /// In general this should be used for determining equality between doubles.      /// </summary>      /// <param name="x0">The orgin of intrest</param>      /// <param name="x"> The point of intrest</param>      /// <param name="epsilon">The minimum distance between the points</param>      /// <returns>Returns true iff x  in (x0-epsilon, x0+epsilon)</returns>      public static bool IsInNeghborhood(double x0, double x, double epsilon) => Abs(x0 - x) < epsilon;        public static bool AreEqual(double v0, double v1) => IsInNeghborhood(v0, v1, _epsilon);  

With these two helpers in place and assuming that if any number can be cast as a double without the required accuracy. All you will need now is an enum and another method

    public enum BoundType      {          Open,          Closed,          OpenClosed,          ClosedOpen      }  

The other method follows:

    public static bool InRange(double value, double upperBound, double lowerBound, BoundType bound = BoundType.Open)      {          bool inside = value < upperBound && value > lowerBound;          switch (bound)          {              case BoundType.Open:                  return inside;              case BoundType.Closed:                  return inside || AreEqual(value, upperBound) || AreEqual(value, lowerBound);               case BoundType.OpenClosed:                  return inside || AreEqual(value, upperBound);              case BoundType.ClosedOpen:                  return inside || AreEqual(value, lowerBound);              default:                  throw new System.NotImplementedException("You forgot to do something");          }      }  

Now this may be far more than what you wanted, but it keeps you from dealing with rounding all the time and trying to remember if a value has been rounded and to what place. If you need to you can easily extend this to work with any epsilon and to allow your epsilon to change.


Solution:21

Elegant because it doesn't require you to determine which of the two boundary values is greater first. It also contains no branches.

public static bool InRange(float val, float a, float b)  {      // Determine if val lies between a and b without first asking which is larger (a or b)      return ( a <= val & val < b ) | ( b <= val & val < a );  }  


Solution:22

I don't know but i use this method:

    public static Boolean isInRange(this Decimal dec, Decimal min, Decimal max, bool includesMin = true, bool includesMax = true ) {        return (includesMin ? (dec >= min) : (dec > min)) && (includesMax ? (dec <= max) : (dec < max));  }  

And this is the way I can use it:

    [TestMethod]      public void IsIntoTheRange()      {          decimal dec = 54;            Boolean result = false;            result = dec.isInRange(50, 60); //result = True          Assert.IsTrue(result);            result = dec.isInRange(55, 60); //result = False          Assert.IsFalse(result);            result = dec.isInRange(54, 60); //result = True          Assert.IsTrue(result);            result = dec.isInRange(54, 60, false); //result = False          Assert.IsFalse(result);            result = dec.isInRange(32, 54, false, false);//result = False          Assert.IsFalse(result);            result = dec.isInRange(32, 54, false);//result = True          Assert.IsTrue(result);      }  


Solution:23

You are looking for in [1..100]? That's only Pascal.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »