I know it's a slightly "Please do my homework" question, but I'm trying to implement the De Boor algorithm as explained here: http://en.wikipedia.org/wiki/De_Boor's_algorithm
I have got a generic class which takes any array of any type and two functions which yield an x and y for each point.
I am getting very inaccurate interpolations when I supply to GetPoint(x) an x which is very near to the lowest x value in _d.
Is anyone able to see what I'm doing wrong? Many thanks in advance.
The data I'm using are:
x y
0.002739726 0.999988548
0.019178082 0.999917365
0.038356164 0.999835128
0.057534247 0.999753381
0.082191781 0.999648832
0.167123288 0.999286638
0.249315068 0.998939303
0.334246575 0.998583164
0.419178082 0.998222171
0.495890411 0.997899634
0.747945205 0.99680615
1 0.99559971
1.249315068 0.994187653
1.495890411 0.9926708
1.747945205 0.99066351
2 0.988439344
2.249315068 0.985377424
2.498630137 0.981972695
2.750684932 0.978188863
3.002739726 0.974069647
4.002739726 0.952415995
5.002739726 0.926226504
6.002739726 0.897271422
7.005479452 0.866241091
8.005479452 0.834348616
9.005479452 0.802369725
The code
public class Spline<T>
{
/// <summary>
/// Coordinate
/// </summary>
private struct TwoDPoint
{
public double x;
public double y;
};
/// <summary>
/// Unused apart from debugging.
/// </summary>
private T [] _originalDataPoints;
/// <summary>
/// Stores the points we will perform the algorithm on.
/// </summary>
private TwoDPoint [] _d;
private int _n; // Value for the "order" used in the De Boor algorithm
public Spline(T [] dataPoints, Func<T, double> xFunc, Func<T, double> yFunc, int n)
{
SortAndAssignPoints(dataPoints, xFunc, yFunc);
_n = n;
}
/// <summary>
/// Populates points in d sorted ascending by their x value
/// </summary>
private void SortAndAssignPoints(T [] dataPoints, Func<T, double> xFunc, Func<T, double> yFunc)
{
_originalDataPoints = dataPoints;
_d = dataPoints
.Select(point => new TwoDPoint()
{
x = xFunc(point),
y = yFunc(point)
})
.OrderBy(point => point.x).ToArray();
}
/// <summary>
/// Function which gets an interpolated value
/// </summary>
public double GetPoint(double x)
{
double sum = 0;
for (int i = 0; i < _d.Length; i++)
{
sum += _d[i].y * N_General(_n, i, x);
}
return sum;
}
/// <summary>
/// General N function as given in the algorithm
/// </summary>
private double N_General(int n, int i, double x)
{
if (n == 0)
{
return N_Base(i, x);
}
double firstCoefficient;
if ((i + n) > (_d.Length - 1))
{
firstCoefficient = 0D;
}
else
{
firstCoefficient =
(x - _d[i].x) / (_d[i + n].x - _d[i].x);
}
double secondCoefficient;
if ((i + n + 1) > (_d.Length - 1))
{
secondCoefficient = 0D;
}
else
{
secondCoefficient =
(_d[i + n + 1].x - x) / (_d[i + n + 1].x - _d[i + 1].x);
}
return firstCoefficient * N_General(n - 1, i, x) +
secondCoefficient * N_General(n - 1, i + 1, x);
}
/// <summary>
/// N_i^0 as given in the algorithm
/// </summary>
private double N_Base(int i, double x)
{
//Bound check
if (
(i >= (_d.Length - 1)) ||
(i < 0)
)
{
return 0;
}
if ((x >= _d[i].x) &&
(x < _d[i+1].x))
{
return 1;
}
else
{
return 0;
}
}
}
From your codes, it looks like you are using y values from _d as the control points and x values from _d as the knot values. Please note that for B-spline curves the control points in general do not lie on the curve itself. So, you should not expect that passing an x value from your data array to GetPoint(x) function will get back the corresponding y value in the data array.
Also, for B-spline curves, the rule of "number of knots = number of control points + order" should always be followed. You really cannot use_d[i].x as knots and _d[i].y as control points as you will have the same number of knots and control points.
Finally, I would recommend using this as your reference instead of the Wikipedia link as it offers better description of De Boor-Cox algorithm.
Related
I am working on a system that needs to accept and display complex fractions. The code for accepting fractions and turning them in to a double works, but when I want to display that value, I need to convert back to a fractional representation.
EDIT: I have fixed the overflow problem, but that didnt solve fractions like 1/3 or 5/6. SO I have devised a very hacky way to do this. I have code which generates the decimal representation of every fraction 0->64 over 1->64, and saves the most simplified form. This way, I can iterate through the list and find the closest fraction, and simply display that. Will post code once I have some.
I have code now that works for the vast majority of numbers, but occasionally I will get a tiny fraction like 1/321. This gets converted to a double, but cannot be converted back, because in my approach, the numerator causes an integer overflow.
Here is my code, I'm wondering if there is a better approach, or if there is someway to safely convert these to longs without losing the precision needed for a correct result:
public static String DecimalToFraction(double dec)
{
string str = dec.ToString();
if (str.Contains('.'))
{
String[] parts = str.Split('.');
long whole = long.Parse(parts[0]);
long numerator = long.Parse(parts[1]);
long denominator = (long)Math.Pow(10, parts[1].Length);
long divisor = GCD(numerator, denominator);
long num = numerator / divisor;
long den = denominator / divisor;
String fraction = num + "/" + den;
if (whole > 0)
{
return whole + " " + fraction;
}
else
{
return fraction;
}
}
else
{
return str;
}
}
public static long GCD(long a, long b)
{
return b == 0 ? a : GCD(b, a % b);
}
Recently I had to code a similar scenario. In my case, converting from decimal to rational number had to be a little more mathematically correct so I ended up implementing a Continued Fraction algorithm.
Although it is tailored made to my concrete implementation of RationalNumber, you should get the idea. It's a relatively simple algorithm that works reasonably well for any rational number approximation. Note that the implementation will give you the closest approximation with the required precision.
/// <summary>
/// Represents a rational number with 64-bit signed integer numerator and denominator.
/// </summary>
[Serializable]
public struct RationalNumber : IComparable, IFormattable, IConvertible, IComparable<RationalNumber>, IEquatable<RationalNumber>
{
private const int MAXITERATIONCOUNT = 20;
public RationalNumber(long number) {...}
public RationalNumber(long numerator, long denominator) {...}
public RationalNumber(RationalNumber numerator, RationalNumer denominator) {...}
...
/// <summary>
/// Defines an implicit conversion of a 64-bit signed integer to a rational number.
/// </summary>
/// <param name="value">The value to convert to a rational number.</param>
/// <returns>A rational number that contains the value of the value parameter as its numerator and 1 as its denominator.</returns>
public static implicit operator RationalNumber(long value)
{
return new RationalNumber(value);
}
/// <summary>
/// Defines an explicit conversion of a rational number to a double-precision floating-point number.
/// </summary>
/// <param name="value">The value to convert to a double-precision floating-point number.</param>
/// <returns>A double-precision floating-point number that contains the resulting value of dividing the rational number's numerator by it's denominator.</returns>
public static explicit operator double(RationalNumber value)
{
return (double)value.numerator / value.Denominator;
}
...
/// <summary>
/// Adds two rational numbers.
/// </summary>
/// <param name="left">The first value to add.</param>
/// <param name="right">The second value to add.</param>
/// <returns>The sum of left and right.</returns>
public static RationalNumber operator +(RationalNumber left, RationalNumber right)
{
//First we try directly adding in a checked context. If an overflow occurs we use the least common multiple and return the result. If it overflows again, it
//will be up to the consumer to decide what he will do with it.
//Cost penalty should be minimal as adding numbers that cause an overflow should be very rare.
RationalNumber result;
try
{
long numerator = checked(left.numerator * right.Denominator + right.numerator * left.Denominator);
long denominator = checked(left.Denominator * right.Denominator);
result = new RationalNumber(numerator,denominator);
}
catch (OverflowException)
{
long lcm = RationalNumber.getLeastCommonMultiple(left.Denominator, right.Denominator);
result = new RationalNumber(left.numerator * (lcm / left.Denominator) + right.numerator * (lcm / right.Denominator), lcm);
}
return result;
}
private static long getGreatestCommonDivisor(long i1, long i2)
{
Debug.Assert(i1 != 0 || i2 != 0, "Whoops!. Both arguments are 0, this should not happen.");
//Division based algorithm
long i = Math.Abs(i1);
long j = Math.Abs(i2);
long t;
while (j != 0)
{
t = j;
j = i % j;
i = t;
}
return i;
}
private static long getLeastCommonMultiple(long i1, long i2)
{
if (i1 == 0 && i2 == 0)
return 0;
long lcm = i1 / getGreatestCommonDivisor(i1, i2) * i2;
return lcm < 0 ? -lcm : lcm;
}
...
/// <summary>
/// Returns the nearest rational number approximation to a double-precision floating-point number with a specified precision.
/// </summary>
/// <param name="target">Target value of the approximation.</param>
/// <param name="precision">Minimum precision of the approximation.</param>
/// <returns>Nearest rational number with, at least, the required precision.</returns>
/// <exception cref="System.ArgumentException">Can not find a rational number approximation with specified precision.</exception>
/// <exception cref="System.OverflowException">target is larger than Mathematics.RationalNumber.MaxValue or smaller than Mathematics.RationalNumber.MinValue.</exception>
/// <remarks>It is important to clarify that the method returns the first rational number found that complies with the specified precision.
/// The method is not required to return an exact rational number approximation even if such number exists.
/// The returned rational number will always be in coprime form.</remarks>
public static RationalNumber GetNearestRationalNumber(double target, double precision)
{
//Continued fraction algorithm: http://en.wikipedia.org/wiki/Continued_fraction
//Implemented recursively. Problem is figuring out when precision is met without unwinding each solution. Haven't figured out how to do that.
//Current implementation evaluates a Rational approximation for increasing algorithm depths until precision criteria is met or maximum depth is reached (MAXITERATIONCOUNT)
//Efficiency is probably improvable but this method will not be used in any performance critical code. No use in optimizing it unless there is a good reason.
//Current implementation works reasonably well.
RationalNumber nearestRational = RationalNumber.zero;
int steps = 0;
while (Math.Abs(target - (double)nearestRational) > precision)
{
if (steps > MAXITERATIONCOUNT)
throw new ArgumentException(Strings.RationalMaximumIterationsExceptionMessage, "precision");
nearestRational = getNearestRationalNumber(target, 0, steps++);
}
return nearestRational;
}
private static RationalNumber getNearestRationalNumber(double number, int currentStep, int maximumSteps)
{
long integerPart;
integerPart = checked((long)number);
double fractionalPart = number - integerPart;
while (currentStep < maximumSteps && fractionalPart != 0)
{
return integerPart + new RationalNumber(1, getNearestRationalNumber(1 / fractionalPart, ++currentStep, maximumSteps));
}
return new RationalNumber(integerPart);
}
}
UPDATE: Whoops, forgot to include the operator + code. Fixed it.
You could use BigRational, which Microsoft released under their BCL project on codeplex. It supports arbitrarily large rational numbers, and actually stores it internally as a ratio. The nice thing is that you can treat it largely as a normal numeric type, since all of the operators are overloaded for you.
Interestingly, it lacks a way to print the number as a decimal. I wrote some code that did this, though, in a previous answer of mine. However, there are no guarantees on its performance or quality (I barely remember writing it).
Keep the number as a fraction:
struct Fraction
{
private int _numerator;
private int _denominator;
public int Numerator { get { return _numerator; } }
public int Denominator { get { return _denominator; } }
public double Value { get { return ((double) Numerator)/Denominator; } }
public Fraction( int n, int d )
{
// move negative to numerator.
if( d < 0 )
{
_numerator = -n;
_denominator = -d;
}
else if( d > 0 )
{
_numerator = n;
_denominator = d;
}
else
throw new NumberFormatException( "Denominator cannot be 0" );
}
public void ToString()
{
string ret = "";
int whole = Numerator / Denominator;
if( whole != 0 )
ret += whole + " ";
ret += Math.Abs(Numerator % Denominator) + "/" + Denominator;
return ret;
}
}
Please check these 2 methods:
/// <summary>
/// Converts Decimals into Fractions.
/// </summary>
/// <param name="value">Decimal value</param>
/// <returns>Fraction in string type</returns>
public string DecimalToFraction(double value)
{
string result;
double numerator, realValue = value;
int num, den, decimals, length;
num = (int)value;
value = value - num;
value = Math.Round(value, 5);
length = value.ToString().Length;
decimals = length - 2;
numerator = value;
for (int i = 0; i < decimals; i++)
{
if (realValue < 1)
{
numerator = numerator * 10;
}
else
{
realValue = realValue * 10;
numerator = realValue;
}
}
den = length - 2;
string ten = "1";
for (int i = 0; i < den; i++)
{
ten = ten + "0";
}
den = int.Parse(ten);
num = (int)numerator;
result = SimplifiedFractions(num, den);
return result;
}
/// <summary>
/// Converts Fractions into Simplest form.
/// </summary>
/// <param name="num">Numerator</param>
/// <param name="den">Denominator</param>
/// <returns>Simplest Fractions in string type</returns>
string SimplifiedFractions(int num, int den)
{
int remNum, remDen, counter;
if (num > den)
{
counter = den;
}
else
{
counter = num;
}
for (int i = 2; i <= counter; i++)
{
remNum = num % i;
if (remNum == 0)
{
remDen = den % i;
if (remDen == 0)
{
num = num / i;
den = den / i;
i--;
}
}
}
return num.ToString() + "/" + den.ToString();
}
}
I receive a string which contains the sw version currently running in the system.
I want to do some operations only if the system is running on a certain sw version or
later.
e.g. If system is running sw version 2.D or later (2.E, ..) I do some operations. If system is running lower sw version (2.C, ..), then I don't do it.
How to do this comparison for strings?
There are many ways of doing this, but if you know that the string is in the form x.y or even x.y.z... then I would suggest using a custom StringComparer:
using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace StingComparisons
{
[TestClass]
public class StringComparison
{
[TestMethod]
public void TestMethod1()
{
var a = "2.E";
var b = "2.F";
var c = "2.C";
var d = "1.F";
var e = "3.A";
StringComparer comp = new MyStringComparer();
Assert.IsTrue(b.IsSameOrAfter(a, comp));
Assert.IsFalse(c.IsSameOrAfter(a, comp));
Assert.IsFalse(d.IsSameOrAfter(a, comp));
Assert.IsTrue(e.IsSameOrAfter(a, comp));
Assert.IsTrue(a.IsSameOrAfter(a, comp));
}
[TestMethod]
public void TestMethod2()
{
var a = "2.E.1";
var b = "2.E";
var c = "2.E.2";
var d = "2.F";
var e = "2.D.3";
var f = "3.A";
StringComparer comp = new MyStringComparer();
Assert.IsFalse(b.DotDelimitedIsSameOrAfter(a));
Assert.IsTrue(c.DotDelimitedIsSameOrAfter(a));
Assert.IsTrue(d.DotDelimitedIsSameOrAfter(a));
Assert.IsFalse(e.DotDelimitedIsSameOrAfter(a));
Assert.IsTrue(f.DotDelimitedIsSameOrAfter(a));
Assert.IsTrue(a.DotDelimitedIsSameOrAfter(a));
}
}
public static class stringExtensions
{
public static bool DotDelimitedIsSameOrAfter(this string a, string b)
{
return a.IsSameOrAfter(b, new MyStringComparer());
}
public static bool IsSameOrAfter(this string a, string b, StringComparer comp)
{
return comp.Compare(a, b) <= 0;
}
}
public class MyStringComparer : StringComparer
{
public override int Compare(string x, string y)
{
var partsX = x.Split('.');
var partsY = y.Split('.');
for (int i = 0; i < partsY.Length; i++)
{
if (partsX.Length <= i)
return 1;
var partComp = partsY[i].CompareTo(partsX[i]);
if (partComp != 0)
return partComp;
}
return 0;
}
public override bool Equals(string x, string y)
{
return x.Equals(y);
}
public override int GetHashCode(string obj)
{
return obj.GetHashCode();
}
}
}
I have wrapped the calls in an extension method purely to make it a bit more readable.
Since I was needing just that (and a bit more), I've written the following comparer which compares two alphanumeric version strings:
/// <summary>
/// Compares two alphanumeric version numbers.
/// </summary>
public class AlphanumericVersionComparer : IComparer<string>
{
/// <summary>
/// Compares two alphanumeric version numbers and returns a value
/// indicating whether one is less than, equal to, or greater than the other.
/// </summary>
/// <param name="x">The first alphanumeric version number to compare.</param>
/// <param name="y">The second alphanumeric version number to compare.</param>
/// <returns>A signed integer that indicates the relative values of x and y.</returns>
public int Compare(string x, string y)
{
// Validate parameters
if (x == null) throw new ArgumentNullException("x");
if (y == null) throw new ArgumentNullException("y");
// Test for equality
if (x == y)
return 0;
// Split the different parts of the number
string[] xParts = x.Split('.');
string[] yParts = y.Split('.');
// Compare each parts
AlphanumericComparer alphaNumComparer = new AlphanumericComparer();
for (int i = 0, n = Math.Max(xParts.Length, yParts.Length); i < n; i++)
{
// If the part at index i is not in y => x is greater
if (i >= yParts.Length)
return 1;
// If the part at index i is not in x => y is greater
if (i >= xParts.Length)
return -1;
// Compare the two alphanumerical numbers
int result = alphaNumComparer.Compare(xParts[i], yParts[i]);
if (result != 0)
{
return result;
}
}
// The two numbers are equal (really??? I thought we tested for equality already!)
System.Diagnostics.Debug.Fail("Not supposed to reach this code...");
return 0;
}
}
/// <summary>
/// Compares two alphanumeric strings.
/// </summary>
/// <remarks>See http://snipd.net/alphanumericnatural-sorting-in-c-using-icomparer </remarks>
public class AlphanumericComparer : IComparer<string>
{
/// <summary>
/// Compares two alphanumerics and returns a value
/// indicating whether one is less than, equal to, or greater than the other.
/// </summary>
/// <param name="x">The first alphanumeric to compare.</param>
/// <param name="y">The second alphanumeric to compare.</param>
/// <returns>A signed integer that indicates the relative values of x and y.</returns>
public int Compare(string x, string y)
{
int len1 = x.Length;
int len2 = y.Length;
int marker1 = 0;
int marker2 = 0;
// Walk through two the strings with two markers.
while (marker1 < len1 && marker2 < len2)
{
char ch1 = x[marker1];
char ch2 = y[marker2];
// Some buffers we can build up characters in for each chunk.
char[] space1 = new char[len1];
int loc1 = 0;
char[] space2 = new char[len2];
int loc2 = 0;
// Walk through all following characters that are digits or
// characters in BOTH strings starting at the appropriate marker.
// Collect char arrays.
do
{
space1[loc1++] = ch1;
marker1++;
if (marker1 < len1)
{
ch1 = x[marker1];
}
else
{
break;
}
} while (char.IsDigit(ch1) == char.IsDigit(space1[0]));
do
{
space2[loc2++] = ch2;
marker2++;
if (marker2 < len2)
{
ch2 = y[marker2];
}
else
{
break;
}
} while (char.IsDigit(ch2) == char.IsDigit(space2[0]));
// If we have collected numbers, compare them numerically.
// Otherwise, if we have strings, compare them alphabetically.
string str1 = new string(space1);
string str2 = new string(space2);
int result;
if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]))
{
int thisNumericChunk = int.Parse(str1);
int thatNumericChunk = int.Parse(str2);
result = thisNumericChunk.CompareTo(thatNumericChunk);
}
else
{
result = str1.CompareTo(str2);
}
if (result != 0)
{
return result;
}
}
return len1 - len2;
}
}
You can play with the fiddle here: https://dotnetfiddle.net/28iius.
All credits to http://snipd.net/alphanumericnatural-sorting-in-c-using-icomparer for the alphanumeric comparison.
using C# with .NET chart.
I am trying to graph several waveforms, and I wish to move my mouse across the chart area, and have my tooltip display the Y value of each series in the chart at this X value location.
| at xValue 12 | |
| _ = 3 | |
| * = 2 | * * |
| ________|______________________________*_____ |
| / | * |
| __________*/*********|***************************** |
| * | |
| * | |
|______________________|_____________________________________|
Kind of like this diagram above. Below is a version of my code:
void chart1_MouseMove(object sender, MouseEventArgs e)
{
var pos = e.Location;
_point.X = e.Location.X;
_point.Y = e.Location.Y;
try
{
if ((chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.X) >= 0) && (chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.X) <= max))
{
//Crossair
chart1.ChartAreas[0].CursorX.SetCursorPixelPosition(_point, true);
//Tooltips
double xValue = chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.X);
double yValue = chart1.ChartAreas[0].AxisY.PixelPositionToValue(e.Y);
string all_Data_Values = "";
foreach (var series in chart1.Series)
{
all_Data_Values = all_Data_Values + Environment.NewLine + series.Name + ": " + yValue;
}
tooltip.Show("At " + Math.Truncate(xValue * 1000) / 1000 + all_Data_Values, this.chart1, pos.X - 40, pos.Y - 20);
}
}
catch (Exception exception)
{
//
}
}
This is what I have, and right now it only displays the Y value of my mouse cursor location. I have tried other codes, trying to somehow map the x values into chart1.Series[] but it didn't work either.
(This is in response to the request for code to look up the nearest value given a pixel coord.)
I'm doing it a bit differently from you, because I'm actually setting the chart's "cursor" as the user moves the mouse around, but hopefully this will give you enough information for you to adapt it to your needs...
Here's how I calculate the X axis coord for a client X coord:
private double calcCursorGraphX(int clientX)
{
var xAxis = _chart.ChartAreas[CHART_INDEX].AxisX;
int xRight = (int) xAxis.ValueToPixelPosition(xAxis.Maximum) - 1;
int xLeft = (int) xAxis.ValueToPixelPosition(xAxis.Minimum);
if (clientX > xRight)
{
return xAxis.Maximum;
}
else if (clientX < xLeft)
{
return xAxis.Minimum;
}
else
{
return xAxis.PixelPositionToValue(clientX);
}
}
Given an X value returned from the above method, we can look up the nearest preceeding value:
private int nearestPreceedingValue(double x)
{
var bpData = _chart.Series[SERIES_INDEX].Points;
int bpIndex = bpData.BinarySearch(x, (xVal, point) => Math.Sign(x - point.XValue));
if (bpIndex < 0)
{
bpIndex = ~bpIndex; // BinarySearch() returns the index of the next element LARGER than the target.
bpIndex = Math.Max(0, bpIndex-1); // We want the value of the previous element, so we must decrement the returned index.
} // If this is before the start of the graph, use the first valid data point.
return bpIndex;
}
Then you have an index which you can use to look up the value from _chart.Series[SERIES_INDEX].Points
I'm not sure if this fits with the way that your data is stored in the charts, but that's how I do it.
[EDIT] Here's the missing BinarySearch extension method. Add it to a static class somewhere accessible. Replace the "Contracts" stuff with your own error handling if you're not using Code Contracts.
/// <summary>
/// Searches the entire sorted IList{T} for an element using the specified comparer
/// and returns the zero-based index of the element.
/// </summary>
/// <typeparam name="TItem">The type of the item.</typeparam>
/// <typeparam name="TSearch">The type of the searched item.</typeparam>
/// <param name="list">The list to be searched.</param>
/// <param name="value">The value to search for.</param>
/// <param name="comparer">The comparer that is used to compare the value with the list items.</param>
/// <returns>
/// The zero-based index of item in the sorted IList{T}, if item is found;
/// otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item,
/// or - if there is no larger element - the bitwise complement of Count.
/// </returns>
public static int BinarySearch<TItem, TSearch>(this IList<TItem> list, TSearch value, Func<TSearch, TItem, int> comparer)
{
Contract.Requires(list != null);
Contract.Requires(comparer != null);
int lower = 0;
int upper = list.Count - 1;
while (lower <= upper)
{
int middle = lower + (upper - lower) / 2;
int comparisonResult = comparer(value, list[middle]);
if (comparisonResult < 0)
{
upper = middle - 1;
}
else if (comparisonResult > 0)
{
lower = middle + 1;
}
else
{
return middle;
}
}
return ~lower;
}
Google doesn't understand that "between" is the name of the function I'm looking for and returns nothing relevant.
Ex: I want to check if 5 is between 0 and 10 in only one operation
It isn't clear what you mean by "one operation", but no, there's no operator / framework method that I know of to determine if an item is within a range.
You could of course write an extension-method yourself. For example, here's one that assumes that the interval is closed on both end-points.
public static bool IsBetween<T>(this T item, T start, T end)
{
return Comparer<T>.Default.Compare(item, start) >= 0
&& Comparer<T>.Default.Compare(item, end) <= 0;
}
And then use it as:
bool b = 5.IsBetween(0, 10); // true
No, but you can write your own:
public static bool Between(this int num, int lower, int upper, bool inclusive = false)
{
return inclusive
? lower <= num && num <= upper
: lower < num && num < upper;
}
Here's a complete class.
/// <summary>
/// An extension class for the between operation
/// name pattern IsBetweenXX where X = I -> Inclusive, X = E -> Exclusive
///
/// </summary>
public static class BetweenExtensions
{
/// <summary>
/// Between check <![CDATA[min <= value <= max]]>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value">the value to check</param>
/// <param name="min">Inclusive minimum border</param>
/// <param name="max">Inclusive maximum border</param>
/// <returns>return true if the value is between the min & max else false</returns>
public static bool IsBetweenII<T>(this T value, T min, T max) where T:IComparable<T>
{
return (min.CompareTo(value) <= 0) && (value.CompareTo(max) <= 0);
}
/// <summary>
/// Between check <![CDATA[min < value <= max]]>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value">the value to check</param>
/// <param name="min">Exclusive minimum border</param>
/// <param name="max">Inclusive maximum border</param>
/// <returns>return true if the value is between the min & max else false</returns>
public static bool IsBetweenEI<T>(this T value, T min, T max) where T:IComparable<T>
{
return (min.CompareTo(value) < 0) && (value.CompareTo(max) <= 0);
}
/// <summary>
/// between check <![CDATA[min <= value < max]]>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value">the value to check</param>
/// <param name="min">Inclusive minimum border</param>
/// <param name="max">Exclusive maximum border</param>
/// <returns>return true if the value is between the min & max else false</returns>
public static bool IsBetweenIE<T>(this T value, T min, T max) where T:IComparable<T>
{
return (min.CompareTo(value) <= 0) && (value.CompareTo(max) < 0);
}
/// <summary>
/// between check <![CDATA[min < value < max]]>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value">the value to check</param>
/// <param name="min">Exclusive minimum border</param>
/// <param name="max">Exclusive maximum border</param>
/// <returns>return true if the value is between the min & max else false</returns>
public static bool IsBetweenEE<T>(this T value, T min, T max) where T:IComparable<T>
{
return (min.CompareTo(value) < 0) && (value.CompareTo(max) < 0);
}
}
plus some unit test code
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethodIsBeetween()
{
Assert.IsTrue(5.0.IsBetweenII(5.0, 5.0));
Assert.IsFalse(5.0.IsBetweenEI(5.0, 5.0));
Assert.IsFalse(5.0.IsBetweenIE(5.0, 5.0));
Assert.IsFalse(5.0.IsBetweenEE(5.0, 5.0));
Assert.IsTrue(5.0.IsBetweenII(4.9, 5.0));
Assert.IsTrue(5.0.IsBetweenEI(4.9, 5.0));
Assert.IsFalse(5.0.IsBetweenIE(4.9, 5.0));
Assert.IsFalse(5.0.IsBetweenEE(4.9, 5.0));
Assert.IsTrue(5.0.IsBetweenII(5.0, 5.1));
Assert.IsFalse(5.0.IsBetweenEI(5.0, 5.1));
Assert.IsTrue(5.0.IsBetweenIE(5.0, 5.1));
Assert.IsFalse(5.0.IsBetweenEE(5.0, 5.1));
Assert.IsTrue(5.0.IsBetweenII(4.9, 5.1));
Assert.IsTrue(5.0.IsBetweenEI(4.9, 5.1));
Assert.IsTrue(5.0.IsBetweenIE(4.9, 5.1));
Assert.IsTrue(5.0.IsBetweenEE(4.9, 5.1));
Assert.IsFalse(5.0.IsBetweenII(5.1, 4.9));
Assert.IsFalse(5.0.IsBetweenEI(5.1, 4.9));
Assert.IsFalse(5.0.IsBetweenIE(5.1, 4.9));
Assert.IsFalse(5.0.IsBetweenEE(5.1, 4.9));
}
}
Nope, you'll have to test each endpoint individually.
if ((x > 0) && (x < 10)) {
// do stuff
}
Or if you want it to look more "betweeny", reorder the args:
if ((0 < x) && (x < 10)) {
// do stuff
}
So far, it looks like none of the answers have considered the likely possibility that dynamically, you don't know which value is the lower and upper bound. For the general case, you could create your own IsBetween method that would probably go something like:
public bool IsBetween(double testValue, double bound1, double bound2)
{
return (testValue >= Math.Min(bound1,bound2) && testValue <= Math.Max(bound1,bound2));
}
As of c# 9, you can do 5 is > 0 and < 10; https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#pattern-matching-enhancements
There is no built in construct in C#/.NET, but you can easily add your own extension method for this:
public static class ExtensionsForInt32
{
public static bool IsBetween (this int val, int low, int high)
{
return val > low && val < high;
}
}
Which can be used like:
if (5.IsBetween (0, 10)) { /* Do something */ }
Except for the answer by #Ed G, all of the answers require knowing which bound is the lower and which is the upper one.
Here's a (rather non-obvious) way of doing it when you don't know which bound is which.
/// <summary>
/// Method to test if a value is "between" two other values, when the relative magnitude of
/// the two other values is not known, i.e., number1 may be larger or smaller than number2.
/// The range is considered to be inclusive of the lower value and exclusive of the upper
/// value, irrespective of which parameter (number1 or number2) is the lower or upper value.
/// This implies that if number1 equals number2 then the result is always false.
///
/// This was extracted from a larger function that tests if a point is in a polygon:
/// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
/// </summary>
/// <param name="testValue">value to be tested for being "between" the other two numbers</param>
/// <param name="number1">one end of the range</param>
/// <param name="number2">the other end of the range</param>
/// <returns>true if testValue >= lower of the two numbers and less than upper of the two numbers,
/// false otherwise, incl. if number1 == number2</returns>
private static bool IsInRange(T testValue, T number1, T number2)
{
return (testValue <= number1) != (testValue <= number2);
}
Note: This is NOT a generic method; it is pseudo code. The T in the above method should be replaced by a proper type, "int" or "float" or whatever. (There are ways of making this generic, but they're sufficiently messy that it's not worth while for a one-line method, at least not in most situations.)
Wouldn't it be as simple as
0 < 5 && 5 < 10
?
So I suppose if you want a function out of it you could simply add this to a utility class:
public static bool Between(int num, int min, int max) {
return min < num && num < max;
}
Generic function that is validated at compilation!
public static bool IsBetween<T>(this T item, T start, T end) where T : IComparable
{
return item.CompareTo(start) >= 0 && item.CompareTo(end) <= 0;
}
int val_to_check = 5
bool in_range = Enumerable.Range(0, 13).Contains(val_to_check);
The second parameter is the "count" not the end or high number.
I.E.
int low_num = 0
int high_num = 12
int val_to_check = 5
bool in_range = Enumerable.Range(low_num , high_num - low_num + 1).Contains(val_to_check);
Checks if the val_to_check is between 0 and 12
What about
somenumber == Math.Max(0,Math.Min(10,somenumber));
returns true when somenumber is 5.
returns false when somenumber is 11.
Refer to this link. A one line solution to that question.
How to elegantly check if a number is within a range?
int x = 30;
if (Enumerable.Range(1,100).Contains(x))
//true
if (x >= 1 && x <= 100)
//true
I know the post is pretty old, but It may help others..
I don't know that function; anyway if your value is unsigned, just one operation means (val < 11)... If it is signed, I think there is no atomic way to do it because 10 is not a power of 2...
As #Hellfrost pointed out, it is literally nonsense to compare a number to two different numbers in "one operation", and I know of no C# operator that encapsulates this.
between = (0 < 5 && 5 < 10);
Is about the most-compact form I can think of.
You could make a somewhat "fluent"-looking method using extension (though, while amusing, I think it's overkill):
public static bool Between(this int x, int a, int b)
{
return (a < x) && (x < b);
}
Use:
int x = 5;
bool b = x.Between(0,10);
Base #Dan J this version don't care max/min, more like sql :)
public static bool IsBetween(this decimal me, decimal a, decimal b, bool include = true)
{
var left = Math.Min(a, b);
var righ = Math.Max(a, b);
return include
? (me >= left && me <= righ)
: (me > left && me < righ)
;
}
Or if you want to bound the value to the interval:
public static T EnsureRange<T>(this T value, T min, T max) where T : IComparable<T>
{
if (value.CompareTo(min) < 0)
return min;
else if (value.CompareTo(max) > 0)
return max;
else
return value;
}
It is like two-sided Math.Min/Max
And for negatives values:
Public Function IsBetween(Of T)(item As T, pStart As T, pEnd As T) As Boolean ' https://msdn.microsoft.com/fr-fr/library/bb384936.aspx
Dim Deb As T = pStart
Dim Fin As T = pEnd
If (Comparer(Of T).Default.Compare(pStart, pEnd) > 0) Then Deb = pEnd : Fin = pStart
Return Comparer(Of T).Default.Compare(item, Deb) >= 0 AndAlso Comparer(Of T).Default.Compare(item, Fin) <= 0
End Function
This question already has answers here:
What is the algorithm to convert an Excel Column Letter into its Number?
(11 answers)
Closed 9 years ago.
I was wondering what is the best way to convert excel sheet column names into numbers.
I'm working with Excel Package, a good library to handle .xlsx documents. This library unfortunately doesn't have this functionality included.
OBS: The first column, A, corresponds
to number 1 in this library.
This function should work for an arbitrary length column name.
public static int GetColumnNumber(string name)
{
int number = 0;
int pow = 1;
for (int i = name.Length - 1; i >= 0; i--)
{
number += (name[i] - 'A' + 1) * pow;
pow *= 26;
}
return number;
}
I had to deal with this a few months ago. The inverse - column index to column name - is fun, too, and becomes really messy if you try to solve it with a zero based index not recognizing that this complicates things. It could be so simple if it would be a normal polyadic numeral system ...
Here is a simplified version of my solution as a extension method without error handling and all that stuff.
public static Int32 ToOneBasedIndex(this String name)
{
return name.ToUpper().
Aggregate(0, (column, letter) => 26 * column + letter - 'A' + 1);
}
I've been working with this for a while now and found this to work really good for columns that go beyond A-Z, or even beyond AA-ZZ... It's accomplished by breaking apart each character in the string and recursively calling itself to derive the DEC value of the ASCII character (less 64), then multiplying it by 26^n. A return value of long was used to overcome a potential limitation when n > 4.
public long columnNumber(String columnName)
{
char[] chars = columnName.ToUpper().ToCharArray();
return (long)(Math.Pow(26, chars.Count() - 1)) *
(System.Convert.ToInt32(chars[0]) - 64) +
((chars.Count() > 2) ? columnNumber(columnName.Substring(1, columnName.Length - 1)) :
((chars.Count() == 2) ? (System.Convert.ToInt32(chars[chars.Count() - 1]) - 64) : 0));
}
Also, if you'd like to get the inverse (i.e. pass in the columnNumber and get the columnName, here's some code that works for that.
public String columnName(long columnNumber)
{
StringBuilder retVal = new StringBuilder();
int x = 0;
for (int n = (int)(Math.Log(25*(columnNumber + 1))/Math.Log(26)) - 1; n >= 0; n--)
{
x = (int)((Math.Pow(26,(n + 1)) - 1) / 25 - 1);
if (columnNumber > x)
retVal.Append(System.Convert.ToChar((int)(((columnNumber - x - 1) / Math.Pow(26, n)) % 26 + 65)));
}
return retVal.ToString();
}
Source code:
namespace XLS
{
/// <summary>
/// Represents a single cell in a excell sheet
/// </summary>
public struct Cell
{
private long row;
private long column;
private string columnAddress;
private string address;
private bool dataChange;
/// <summary>
/// Initializes a new instance of the XLS.Cell
/// class with the specified row and column of excel worksheet
/// </summary>
/// <param name="row">The row index of a cell</param>
/// <param name="column">The column index of a cell</param>
public Cell(long row, long column)
{
this.row = row;
this.column = column;
dataChange = true;
address = string.Empty;
columnAddress = string.Empty;
}
/// <summary>
/// Initializes a new instance of the XLS.Cell
/// class with the specified address of excel worksheet
/// </summary>
/// <param name="address">The adress of a cell</param>
public Cell(string address)
{
this.address = address;
dataChange = false;
row = GetRow(address);
columnAddress = GetColumnAddress(address);
column = GetColumn(columnAddress);
}
/// <summary>
/// Gets or sets the row of this XLS.Cell
/// </summary>
public long Row
{
get { return row <= 0 ? 1 : row; }
set { row = value; dataChange = true; }
}
/// <summary>
/// Gets or sets the column of this XLS.Cell
/// </summary>
public long Column
{
get { return column <= 0 ? 1 : column; }
set { column = value; dataChange = true; }
}
/// <summary>
/// Gets or sets the address of this XLS.Cell
/// </summary>
public string Address
{
get { return dataChange ? ToAddress() : address; }
set
{
address = value;
row = GetRow(address);
column = GetColumn(address);
}
}
/// <summary>
/// Gets the column address of this XLS.Cell
/// </summary>
public string ColumnAddress
{
get { return GetColumnAddress(Address); }
private set { columnAddress = value; }
}
#region Private Methods
private static long GetRow(string address)
{
return long.Parse(address.Substring(GetStartIndex(address)));
}
private static string GetColumnAddress(string address)
{
return address.Substring(0, GetStartIndex(address)).ToUpperInvariant();
}
private static long GetColumn(string columnAddress)
{
char[] characters = columnAddress.ToCharArray();
int sum = 0;
for (int i = 0; i < characters.Length; i++)
{
sum *= 26;
sum += (characters[i] - 'A' + 1);
}
return (long)sum;
}
private static int GetStartIndex(string address)
{
return address.IndexOfAny("123456789".ToCharArray());
}
private string ToAddress()
{
string indexToString = string.Empty;
if (Column > 26)
{
indexToString = ((char)(65 + (int)((Column - 1) / 26) - 1)).ToString();
}
indexToString += (char)(65 + ((Column - 1) % 26));
dataChange = false;
return indexToString + Row;
}
#endregion
}
}
O24 has a column number and you want a name:
=LEFT(RIGHT(ADDRESS(1,O24),LEN(ADDRESS(1,O24))-1),FIND("$",RIGHT((ADDRESS(1,O24)),LEN(ADDRESS(1,O24))-1))-1)
O37 has a column name and you want a number:
=COLUMN(INDIRECT(O37&1))
public static string GetColumnName(int index)
{
const string letters = "ZABCDEFGHIJKLMNOPQRSTUVWXY";
int NextPos = (index / 26);
int LastPos = (index % 26);
if (LastPos == 0) NextPos--;
if (index > 26)
return GetColumnName(NextPos) + letters[LastPos];
else
return letters[LastPos] + "";
}