The disadvantage of the code below is that it calculates the RSI for all prices at once. What if I have to add a new price? It's going to recalculate everything just because one additional price in the array. It will take longer than if I backup AverageGain/AverageLoss.
public class RSI
{
private readonly int _period;
public RSI(int period)
{
_period = period;
}
public decimal[] Calculate(decimal[] prices)
{
var rsi = new decimal[prices.Length];
decimal gain = 0m;
decimal loss = 0m;
rsi[0] = 0m;
for (int i = 1; i <= _period; ++i)
{
var diff = prices[i] - prices[i - 1];
if (diff >= 0)
{
gain += diff;
}
else
{
loss -= diff;
}
}
decimal avrg = gain / _period;
decimal avrl = loss / _period;
decimal rs = gain / loss;
rsi[_period] = 100m - (100m / (1m + rs));
for (int i = _period + 1; i < prices.Length; ++i)
{
var diff = prices[i] - prices[i - 1];
if (diff >= 0)
{
avrg = ((avrg * (_period - 1)) + diff) / _period;
avrl = (avrl * (_period - 1)) / _period;
}
else
{
avrl = ((avrl * (_period - 1)) - diff) / _period;
avrg = (avrg * (_period - 1)) / _period;
}
rs = avrg / avrl;
rsi[i] = 100m - (100m / (1m + rs));
}
return rsi;
}
}
My idea is to make something that remembers the previous input and calculate the RSI based on it, just like this one. Right now, this code is not working because _period is not involved at all. Those people in that github made it, but I'm still struggling to do it because of all these inherited classes/interfaces. I don't want to implement all of the interfaces/abstract classes. The one I extracted below are enough for me.
// Trying to achieve this:
var candles = _client.GetKlines(bot.Symbol, KlineInterval.OneHour).Data.ToList();
RelativeStrengthIndex rsi = new RelativeStrengthIndex(12);
for (int i = 0; i < candles.Count - 1; i++)
{
var result = rsi.ComputeNextValue(new InputData(candles[i].Close));
Console.WriteLine($"RSI(12) = {result}");
}
// New candle data
var newResult = rsi.ComputeNextValue(new InputData(0.01256m));
Console.WriteLine($"RSI(12) = {newResult}");
public abstract class Indicator
{
public abstract decimal ComputeNextValue(InputData input);
public abstract void Reset();
}
public class InputData
{
public decimal Value { get; private set; }
public InputData(decimal value)
{
Value = value;
}
}
public class RelativeStrengthIndex : Indicator
{
private int _period;
private InputData _previousInput;
public decimal AverageGain { get; private set; }
public decimal AverageLoss { get; private set; }
public RelativeStrengthIndex(int period)
{
_period = period;
}
public override decimal ComputeNextValue(InputData input)
{
if (_previousInput != null && input.Value >= _previousInput.Value)
{
AverageGain = input.Value - _previousInput.Value;
AverageLoss = 0m;
}
else if (_previousInput != null && input.Value < _previousInput.Value)
{
AverageGain = 0m;
AverageLoss = _previousInput.Value - input.Value;
}
_previousInput = input;
if (AverageLoss == 0m)
{
return 100m;
}
var rs = AverageGain / AverageLoss;
return 100m - (100m / (1 + rs));
}
public override void Reset()
{
_previousInput = default;
}
}
I did it myself. If you have any suggestions/improvements, please note them in the comments.
public class RelativeStrengthIndex : Indicator
{
private readonly int _period;
private InputData _previousInput;
private int _index;
private decimal _gain;
private decimal _loss;
private decimal _averageGain;
private decimal _averageLoss;
public RelativeStrengthIndex(int period)
{
_period = period;
_index = 0;
_gain = 0;
_loss = 0;
_averageGain = 0;
_averageLoss = 0;
}
public override decimal ComputeNextValue(InputData input)
{
// Formula: https://stackoverflow.com/questions/38481354/rsi-vs-wilders-rsi-calculation-problems?rq=1
_index++;
if (_previousInput != null)
{
var diff = input.Value - _previousInput.Value;
_previousInput = input;
if (_index <= _period)
{
if (diff >= 0)
{
_totalGain += diff;
}
else
{
_totalLoss -= diff;
}
}
if (_index < _period)
{
return 0;
}
else if (_index == _period)
{
_averageGain = _totalGain / _period;
_averageLoss = _totalLoss / _period;
decimal rs = _averageGain / _averageLoss;
return 100 - (100 / (1 + rs));
}
else // if (_index >= _period + 1)
{
if (diff >= 0)
{
_averageGain = ((_averageGain * (_period - 1)) + diff) / _period;
_averageLoss = (_averageLoss * (_period - 1)) / _period;
}
else
{
_averageGain = (_averageGain * (_period - 1)) / _period;
_averageLoss = ((_averageLoss * (_period - 1)) - diff) / _period;
}
decimal rs = _averageGain / _averageLoss;
return 100 - (100 / (1 + rs));
}
}
_previousInput = input;
return 0;
}
public override void Reset()
{
_previousInput = null;
_index = 0;
_gain = 0;
_loss = 0;
_averageGain = 0;
_averageLoss = 0;
}
}
Related
I'm having a problem with sorting and IComparer(). My program stops on it. I'm a beginner with C#.
Here is the part of the code where it stops:
public ArrayList ModelSort()
{
IComparer sorter = new R2SortHelper();
InnerList.Sort(sorter);
return InnerList;
}
private class R2SortHelper : System.Collections.IComparer
{
public int Compare(object x, object y)
{
double m1 = ((Model)x).R2() + ((Model)x).Valid().GetHashCode();
double m2 = ((Model)y).R2() + ((Model)y).Valid().GetHashCode();
if (m1 > m2)
return -1;
else if (m1 < m2)
return 1;
else
return 0;
}
}
Here is an error from console:
Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: 'AMO.EnPI.AddIn.Utilities.ModelCollection+R2SortHelper'.
List of exceptions:
Exception Text
System.ArgumentException: Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: 'AMO.EnPI.AddIn.Utilities.ModelCollection+R2SortHelper'.
at System.Array.SorterObjectArray.DepthLimitedQuickSort(Int32 left, Int32 right, Int32 depthLimit)
at System.Array.SorterObjectArray.DepthLimitedQuickSort(Int32 left, Int32 right, Int32 depthLimit)
at System.Array.Sort(Array keys, Array items, Int32 index, Int32 length, IComparer comparer)
at System.Collections.ArrayList.Sort(Int32 index, Int32 count, IComparer comparer)
at System.Collections.ArrayList.Sort(IComparer comparer)
at AMO.EnPI.AddIn.Utilities.ModelCollection.ModelSort() in C:\ENPI\EnPI\AMO.EnPI-5.0\AMO.EnPI.Utilities\Analytics.cs:line 961
at AMO.EnPI.AddIn.ModelSheet.WriteResultsTable(Int32 n, Boolean top) in C:\ENPI\EnPI\AMO.EnPI-5.0\AMO.EnPI.AddIn\ModelSheet.cs:line 146
at AMO.EnPI.AddIn.ModelSheet.Populate() in C:\ENPI\EnPI\AMO.EnPI-5.0\AMO.EnPI.AddIn\ModelSheet.cs:line 60
at AMO.EnPI.AddIn.ThisAddIn.plotEnPI(ListObject LO) in C:\ENPI\EnPI\AMO.EnPI-5.0\AMO.EnPI.AddIn\ThisAddIn.cs:line 318
at AMO.EnPI.AddIn.RegressionControl.runFunction(Object sender, EventArgs e) in C:\ENPI\EnPI\AMO.EnPI-5.0\AMO.EnPI.AddIn\RegressionControl.cs:line 817
at AMO.EnPI.AddIn.CO2EmissionControl.btnCalculate_Click(Object sender, EventArgs e) in C:\ENPI\EnPI\AMO.EnPI-5.0\AMO.EnPI.AddIn\CO2EmissionControl.cs:line 148
My model class:
public class Model
{
public int ModelNumber { get; set; }
public double[] Ys { get; set; }
public double[,] Xs { get; set; }
public string[] VariableNames { get; set; }
public double RMSError { get; set; }
public double[] Coefficients { get; set; }
public Model()
{
ModelNumber = 0;
Ys = null;
Xs = null;
VariableNames = null;
RMSError = 0;
Coefficients = null;
}
public Model(int ModelNumber, double[] Ys, double[,] Xs, string[] VariableNames)
{
RMSError = 0;
Coefficients = null;
// run LLS
int info;
double[] c;
alglib.lsfitreport rep;
try
{
alglib.lsfitlinear(Ys, Xsplusone(), out info, out c, out rep);
}
catch
{
throw;
}
Coefficients = c;
RMSError = rep.rmserror;
}
public void Run() //double[] Ys, double[,] Xs, string[] VariableNames)
{
RMSError = 0;
Coefficients = null;
if (Ys != null && Xs != null)
{
// run LLS
int info;
double[] c;
alglib.lsfitreport rep;
try
{
alglib.lsfitlinear(Ys, Xsplusone(), out info, out c, out rep);
}
catch
{
throw;
}
Coefficients = c;
RMSError = rep.rmserror;
}
}
public int N()
{
return Ys.Count();
}
public int df()
{
return N() - k() - 1;
}
public int k()
{
return VariableNames.Count();
}
public double TotalSS()
{
// compute total sum of squares
double ybar = Ys.Average();
double sst = 0;
for (int i = Ys.GetLowerBound(0); i <= Ys.GetUpperBound(0); i++)
{
sst += Math.Pow(Ys[i] - ybar, 2);
}
return sst;
}
public double ResidualSS ()
{
return ( N() * Math.Pow( RMSError, 2));
}
public double R2()
{
return (1 - (ResidualSS() / TotalSS()));
}
public double AdjustedR2()
{
return (1 - (((1 - R2()) * (N() - 1)) / (N() - k() - 1)));
}
public double F()
{
return ( (R2() / k()) / ((1 - R2()) / (N() - k() - 1)));
}
public double ModelPValue()
{
double modelP = 0;
double modelF = F();
if (modelF < 0) modelF = 0;
try
{
modelP = alglib.fcdistribution(N() - df() - 1, df(), modelF);
}
catch (alglib.alglibexception e)
{
}
return modelP;
}
public bool Valid()
{
// Model validity criteria, from the SEP M&V protocol:
// The model p-value must be less than 0.1
// All variables must have p-values less than 0.2
// At least one variable must have a p-value of less than 0.1
// The R2 value must be greater than 0.5
double[] ps = PValues();
bool varsvalid = true;
bool varlowexists = false;
for (int i = 0; i < ps.Count(); i++)
{
if (ps[i] <= Constants.PVALUE_THRESHOLD)
varlowexists = true;
if (ps[i] > Constants.PVALUE_HIGH)
varsvalid = false;
}
if (!varlowexists)
return false;
if (!varsvalid)
return false;
if (ModelPValue() > Constants.PVALUE_THRESHOLD)
return false;
if (R2() < Constants.R2VALUE_MIN)
return false;
return true;
}
public string Formula()
{
string formula = "";
int offset = Coefficients.GetLowerBound(0) - VariableNames.GetLowerBound(0);
for (int i = Coefficients.GetLowerBound(0); i < Coefficients.GetUpperBound(0); i++)
{
formula += "(" + Coefficients[i].ToString("0.0000") + " * " + ExcelHelpers.CreateValidFormulaName(VariableNames[i - offset]) + ") + ";
// formula += "(" + Coefficients[i].ToString() + " * " + ExcelHelpers.CreateValidFormulaName(VariableNames[i - offset]) + ") + ";
}
formula += Coefficients[Coefficients.GetUpperBound(0)].ToString("0.00");
return formula;
}
public double[,] Xsplusone()
{
return DataHelper.arrayAddIdentity(Xs, 0, 1); // add on a column of ones for the intercept
}
public double[] PredictedYs()
{ // compute the predicted ys
double[] yhat = new double[N()];
double[,] xs = Xsplusone();
double[] c = Coefficients;
for (int i = 0; i < N(); i++)
{
yhat[i] = 0;
for (int j = 0; j < k() + 1; j++)
{
yhat[i] += xs[i, j] * c[j];
}
}
return yhat;
}
public double[,] CovarianceMatrix()
{
// compute the coefficient covariance matrix
double[,] twodYs = DataHelper.dbl2DArray(Ys);
double[,] XYs = DataHelper.dblarrayUnion(Xs, twodYs);
double[,] cov;
int info;
alglib.linearmodel lm;
alglib.lrreport rpt;
try
{
alglib.lrbuild(XYs, N(), k(), out info, out lm, out rpt);
cov = rpt.c;
}
catch
{
throw;
}
return cov;
}
public double[] StandardErrors()
{
// compute the x std errors and p-values
double[,] cov = CovarianceMatrix();
double[] se = new double[k()];
if (cov.GetLength(0) > 0 && cov.GetLength(1) > 0)
{
for (int j = 0; j < k(); j++)
{
se[j] = Math.Sqrt(cov[j, j]);
}
}
return se;
}
public double[] PValues()
{
double[] c = Coefficients;
double[,] cov = CovarianceMatrix();
double[] se = StandardErrors();
double[] pv = new double[k()];
if (cov.GetLength(0) > 0 && cov.GetLength(1) > 0)
{
for (int j = 0; j < k(); j++)
{
se[j] = Math.Sqrt(cov[j, j]);
try
{
pv[j] = 2 * (1 - alglib.studenttdistribution(df(), Math.Abs(c[j] / se[j])));
}
catch
{
}
}
}
return pv;
}
public string AICFormula()
{
return "";
}
//Added By Suman for SEP Validation changes
public string[] SEPValidationCheck()
{
string[] sepChk = new string[k()];
for (int cnt = 0; cnt < sepChk.Length; cnt++)
{
if (Valid() == true)
{
sepChk[cnt] = "Pass";
}
else
{
sepChk[cnt] = "Fail";
}
}
return sepChk;
}
}
Here is GetHashCode():
public override int GetHashCode()
{
return x.GetHashCode() ^ y.GetHashCode();
}
My R2():
public double R2()
{
return (1 - (ResidualSS() / TotalSS()));
}
For the majority of types, you can't sort by GetHashCode() since the hash code calculations aren't necessarily going to sort in the same order as the value. You can only sort on GetHashCode() reliably when it is the value itself (such as int), but at that point there isn't any point in getting the hash code since you already have the value. Also you didn't show what R2() and Valid() methods do, so who knows what kind of unholyness is going on in there.
EDIT (OP updated code):
Your GetHashCode() usage is definitely not sortable. Nor is adding a boolean to a double. 5 + 0 vs. 2 + 1 is not going to sort correctly (assuming you want the falses up front?)
Just use ((Model)x).R2() vs ((Model)y).R2() as the comparison, if you want the falses or trues up front, you can add something like:
if (x.Valid && !y.Valid)
return 1;
if (!x.Valid && y.Valid)
return -1;
//then do your R2 comparisons here.
That preamble will cause the non valids to always come first and then sort by R2s. You can reverse if you want to go the other way.
Your .Valid() method is returning boolean, and then you go to find hash code. Is this expected from your code?
Basically, you are doing some double value + boolean.GetHashCode(). Solution would be to not use .Valid() in your right side expression. Check that separately.
private class R2SortHelper : System.Collections.IComparer
{
public int Compare(object x, object y)
{
Model xModel = (Model)x;
Model yModel = (Model)y;
if((Model.Valid()==true) && (yModel.Valid()==true))
{
double m1 = xModel.R2() + xModel.GetHashCode();
double m2 = yModel.R2() + yModel.GetHashCode();
return (m1 > m2) ? -1 : ((m1 < m2) ? 1 :0);
}
return 0;
}
}
Here is code.
public class Adaline
{
private int _layer;
public int Layer { get { return _layer; } }
private int _epoch;
public int Epoch { get { return _epoch; } }
private double _error;
public double Error { get { return _error; } }
private double[] _weights;
public Adaline(int layer)
{
_layer = layer;
_weights = new double[layer];
Reset();
}
public void Reset()
{
Random r = new Random();
for (int i = 0; i < _layer; i++)
_weights[i] = r.NextDouble() - 0.5;
_error = 1;
}
public void Train(BasicTrainSet<double> trainset, double learnRate)
{
double ers = 0;
for(int p = 0; p < trainset.DataCount; p++)
{
double result = Compute(trainset.Input[p], true);
double error = trainset.Output[p] - result;
for (int i = 0; i < _weights.Length; i++)
{
_weights[i] += error * trainset.Input[p][i] * learnRate;
}
ers += Math.Abs(error);
}
_epoch++;
_error = ers;
}
public double Compute(double[] input, bool quan)
{
double result = 0;
for (int i = 0; i < _layer; i++)
result += Math.Tanh(_weights[i] * input[i]);
//double result = _weights.Zip(input, (a, b) => Math.Tanh(a * b)).Sum();
return quan ? (result >= 0 ? 1 : 0) : result;
}
}
When I tried to train and gate like this, it works like this.
Up four results are from this code
This is pretty weird, because there is not any problem with algorithm.
Weights are getting bigger and bigger. Where did i mistake?
In your code to compute the output of each neuron, you aren't applying the activation function correctly. You need to find the dot product between the weights and the inputs into each neuron then apply the activation function after. You are applying the activation function after each weighted accumulation, which is not correct.
Accumulate, then apply the activation function:
public double Compute(double[] input, bool quan)
{
double result = 0;
for (int i = 0; i < _layer; i++)
result += _weights[i] * input[i]; // Change - accumulate first
result = Math.Tanh(result); // Change - now apply activation function
return quan ? (result >= 0 ? 1 : 0) : result;
}
I am having issues with the output of the the result of a math calculation. I have a basic average of an array of double and I assign the result to a Label object, using the ToString() method. When I emulate the average, the label shows the correct value of 15.96 for example, but the same average of the same array, on my Galaxy S3 shows 159.6.
Is there anyone who know what's up and what can I do to make the S3 show the correct value?
Thank you all!
EDIT: passing the result to a label and adding the label to the grid:
double result = Math.Round(NP122.DoAverage(parameters), 2);
CustomLabel label = new CustomLabel();
label.ColNo = grid.ColumnDefinitions.IndexOf(c);
label.FontSize = 25;
label.TextColor = Color.Green;
if (result.ToString() == "NaN")
label.Text = "0";
else
label.Text = result.ToString();
label.IsVisible = true;
for (int i = 0; i < numberOfRows.Length + 2; i++) {
if(i == numberOfRows.Length +1)
Grid.SetRow(label, i);
}
Grid.SetColumn(label, grid.ColumnDefinitions.IndexOf(c));
listaRez.Add(label);
foreach (CustomLabel a in listaRez)
{
if (a.ColNo == grid.ColumnDefinitions.IndexOf(c))
{
grid.Children.Add(a);
}
}
EDIT 2: Custom function for NP122.DoAverage:
public static class NP122
{
public static double Vx, sx, Xm, kn, Xkinf, Xksup;
public static double sum;
public static double sumaProvizorie;
public static double[] valoriKn = new double[25];
public static double ValoareCaracteristicaSuperioara(double[] l)
{
Vx = 0;
sx = 0;
Xm = 0;
kn = 0;
Xkinf = 0;
Xksup = 0;
sum = 0;
sumaProvizorie = 0;
valoriKn[0] = 0;
//more here
valoriKn[24] = 0.35;
if (l.Length < 2 )
{
Xksup = 0;
Xkinf = 0;
}
else
{
Xm = (l.Sum()) / (l.Length);
for (int j = 0; j < l.Length; j++)
{
sumaProvizorie = Math.Round(Math.Pow((l[j] - Xm), 2), 2);
sum += sumaProvizorie;
}
kn = valoriKn[l.Length - 1];
double elements = (1.00 / (l.Length - 1));
double putere = sum;
sx = Math.Round(Math.Sqrt(elements * putere), 4);
Vx = sx / Xm;
Xksup = Xm * (1 + kn * Vx);
Xkinf = Xm * (1 - kn * Vx);
}
return Xksup;
I am currently developing a program that is using tab same like Google Chrome Tabs,
The tab is working fine, no problem.
But I come into a condition that I can drag the tab even out of monitor screen (left and right edge).
The Question is, how can I restrict the tab dragged into the edge of the screen??
So the tab will not go pass through the screen. Basically it is only about aesthetic purpose..
Be reminded that I have made research from the google and this website but still can't figure it out how to do it..
Here is a piece of the codes.
private bool draggingWindow;
private Size finalSize;
private double overlap;
private double leftMargin;
private double rightMargin;
private double maxTabWidth;
private double minTabWidth;
private double defaultMeasureHeight;
private double currentTabWidth;
private int captureGuard;
private int originalIndex;
private int slideIndex;
private List<double> slideIntervals;
private ChromeTabItem draggedTab;
private Point downPoint;
private ChromeTabControl parent;
public Rect addButtonRect;
private Size addButtonSize;
public Button addButton;
private bool modifyLeftOffset, modifyTopOffset;
private Point origCursorLocation;
private double origHorizOffset, origVertOffset;
protected override void OnPreviewMouseMove(MouseEventArgs e)
{
try
{
base.OnPreviewMouseMove(e);
if (this.addButtonRect.Contains(e.GetPosition(this)) && this.addButton.Background != Brushes.White && this.addButton.Background != Brushes.DarkGray)
{
this.addButton.Background = Brushes.White;
this.InvalidateVisual();
}
else if (!this.addButtonRect.Contains(e.GetPosition(this)) && this.addButton.Background != null)
{
this.addButton.Background = null;
this.InvalidateVisual();
}
if (this.draggedTab == null || this.draggingWindow) { return; }
Point nowPoint = e.GetPosition(this);
this.addButton.Visibility = Visibility.Hidden;
double newHorizontalOffset;
if (this.modifyLeftOffset)
newHorizontalOffset = this.origHorizOffset + (nowPoint.X - this.origCursorLocation.X);
else
newHorizontalOffset = this.origHorizOffset - (nowPoint.X - this.origCursorLocation.X);
Thickness margin = new Thickness(nowPoint.X - this.downPoint.X, 0, this.downPoint.X - nowPoint.X, 0);
this.draggedTab.Margin = margin;
Rect elemRect = this.CalculateDragElementRect(newHorizontalOffset, 1);
bool leftAlign = elemRect.Left < 0;
bool rightAlign = elemRect.Right > this.ActualWidth;
if (leftAlign)
newHorizontalOffset = modifyLeftOffset ? 0 : this.ActualWidth - elemRect.Width;
else if (rightAlign)
newHorizontalOffset = modifyLeftOffset ? this.ActualWidth - elemRect.Width : 0;
if (this.modifyLeftOffset)
Canvas.SetLeft(this.draggedTab, newHorizontalOffset);
else
Canvas.SetRight(this.draggedTab, newHorizontalOffset);
base.OnPreviewMouseMove( e );
if (margin.Left != 0)
{
int guardValue = Interlocked.Increment(ref this.captureGuard);
if (guardValue == 1)
{
this.originalIndex = this.draggedTab.Index;
this.slideIndex = this.originalIndex + 1;
this.slideIntervals = new List<double>();
this.slideIntervals.Add(double.NegativeInfinity);
for (int i = 1; i <= this.Children.Count; i += 1)
{
var diff = i - this.slideIndex;
var sign = diff == 0 ? 0 : diff / Math.Abs(diff);
var bound = Math.Min(1, Math.Abs(diff)) * ((sign * this.currentTabWidth / 3) + ((Math.Abs(diff) < 2) ? 0 : (diff - sign) * (this.currentTabWidth - this.overlap)));
this.slideIntervals.Add(bound);
}
this.slideIntervals.Add(double.PositiveInfinity);
this.CaptureMouse();
}
else
{
int changed = 0;
if (this.slideIntervals != null)
{
if (margin.Left < this.slideIntervals[this.slideIndex - 1])
{
SwapSlideInterval(this.slideIndex - 1);
this.slideIndex -= 1;
changed = 1;
}
else if (margin.Left > this.slideIntervals[this.slideIndex + 1])
{
SwapSlideInterval(this.slideIndex + 1);
this.slideIndex += 1;
changed = -1;
}
}
if (changed != 0)
{
var rightedOriginalIndex = this.originalIndex + 1;
var diff = 1;
if (changed > 0 && this.slideIndex >= rightedOriginalIndex)
{
changed = 0;
diff = 0;
}
else if (changed < 0 && this.slideIndex <= rightedOriginalIndex)
{
changed = 0;
diff = 2;
}
ChromeTabItem shiftedTab = this.Children[this.slideIndex - diff] as ChromeTabItem;
if (shiftedTab != this.draggedTab)
{
StickyReanimate(shiftedTab, changed * (this.currentTabWidth - this.overlap), .13);
}
}
}
}
}
catch (Exception ex)
{
throw ex;
}
}
Any response and attention is really appreciated..
Thank you
Is it possible to display fractions in a NumericUpDown or a DomainUpDown?
I know there are some fonts that have various fraction characters, but I would like to keep my form using Microsoft Sans Serif uniformly.
What do you mean by upto one-tenth.
I have a sample code for you, try if it works for you
nupdwn.Minimum = -10;
nupdwn.Maximum = 10;
nupdwn.Increment = 0.25;
nupdwn.DecimalPlaces = 2;
Microsoft Sans Serif does support fractional characters:
I will try to program a derived NumericUpDown to show fractional values. If it succeeds, i will share the code here...
Here is my Sample-Code for a FractionalUpDown.
Be sure to set DecimalPlaces to your needs.
With Mode, you can choose the formatting of the value:
EMode.Decimal => 2,500.
EMode.Fractional => ⁵⁄₂
EMode.FractionalMixed => 2 ¹⁄₂
EMode.FractionalASCII => 5/2
EMode.FractionalMixedASCII => 2 1/2
Code:
public class FractionalUpDown: NumericUpDown {
//Hide Hexadecimal
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
[Bindable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new bool Hexadecimal {
get { return false; }
set { base.Hexadecimal = false; }
}
private EMode mode;
[DefaultValue(EMode.Fractional)]
public EMode Mode {
get { return mode; }
set {
if (value != mode) {
mode = value;
UpdateEditText();
}
}
}
public enum EMode {
Fractional,
FractionalMixed,
FractionalASCII,
FractionalMixedASCII,
Decimal
}
public FractionalUpDown() {
}
protected override void UpdateEditText() {
if (Mode == EMode.Decimal) {
base.UpdateEditText();
return;
}
double accuracy = Math.Pow(10.0, -(DecimalPlaces + 1));
if (accuracy > 0.1) accuracy = 0.1;
this.Text = FractionToString(DoubleToFraction((double)Value, accuracy), Mode);
}
public struct Fraction {
public Fraction(int n, int d) {
N = n;
D = d;
}
public int N { get; private set; }
public int D { get; private set; }
}
private static readonly char[] numbers = new char[10] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
private static readonly char[] numerators = new char[10] { '⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹' };
private static readonly char[] denumerators = new char[10] { '₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉' };
protected string FractionToString(Fraction frac, EMode mode) {
int full = 0;
int n = frac.N;
string result = string.Empty;
bool mixed = mode == EMode.FractionalMixed ||
mode == EMode.FractionalMixedASCII;
bool useFractionalChars = mode == EMode.Fractional ||
mode == EMode.FractionalMixed;
if (mixed && Math.Abs(frac.N) >= frac.D) {
full = frac.N / frac.D;
n = Math.Abs(frac.N % frac.D);
if (full != 0) result = full.ToString();
else if (n == 0) return "0";
}
if (n != 0) {
string fracNtext = n.ToString();
string fracDtext = frac.D.ToString();
if (useFractionalChars) {
for (int i = 0; i < 10; i++) fracNtext = fracNtext.Replace(numbers[i], numerators[i]);
for (int i = 0; i < 10; i++) fracDtext = fracDtext.Replace(numbers[i], denumerators[i]);
} else {
if (full != 0) result += " ";
}
result += fracNtext + '⁄' + fracDtext;
} else {
//Fractional Part == 0/?
if (full == 0) {
if (mixed == true) {
return "0";
} else {
if (useFractionalChars) {
return numerators[0].ToString() + '⁄' + denumerators[1].ToString();
} else {
return numbers[0].ToString() + '⁄' + numbers[1].ToString();
}
}
}
}
return result;
}
//Source: https://stackoverflow.com/questions/5124743/algorithm-for-simplifying-decimal-to-fractions/32903747#32903747
protected Fraction DoubleToFraction(double value, double accuracy) {
if (accuracy <= 0.0 || accuracy >= 1.0) {
throw new ArgumentOutOfRangeException("accuracy", "Must be > 0 and < 1.");
}
int sign = Math.Sign(value);
if (sign == -1) {
value = Math.Abs(value);
}
// Accuracy is the maximum relative error; convert to absolute maxError
double maxError = sign == 0 ? accuracy : value * accuracy;
int n = (int)Math.Floor(value);
value -= n;
if (value < maxError) {
return new Fraction(sign * n, 1);
}
if (1 - maxError < value) {
return new Fraction(sign * (n + 1), 1);
}
// The lower fraction is 0/1
int lower_n = 0;
int lower_d = 1;
// The upper fraction is 1/1
int upper_n = 1;
int upper_d = 1;
while (true) {
// The middle fraction is (lower_n + upper_n) / (lower_d + upper_d)
int middle_n = lower_n + upper_n;
int middle_d = lower_d + upper_d;
if (middle_d * (value + maxError) < middle_n) {
// real + error < middle : middle is our new upper
Seek(ref upper_n, ref upper_d, lower_n, lower_d, (un, ud) => (lower_d + ud) * (value + maxError) < (lower_n + un));
upper_n = middle_n;
upper_d = middle_d;
} else if (middle_n < (value - maxError) * middle_d) {
// middle < real - error : middle is our new lower
Seek(ref lower_n, ref lower_d, upper_n, upper_d, (ln, ld) => (ln + upper_n) < (value - maxError) * (ld + upper_d));
lower_n = middle_n;
lower_d = middle_d;
} else {
// Middle is our best fraction
return new Fraction((n * middle_d + middle_n) * sign, middle_d);
}
}
}
/// <summary>
/// Binary seek for the value where f() becomes false.
/// Source: https://stackoverflow.com/questions/5124743/algorithm-for-simplifying-decimal-to-fractions/32903747#32903747
/// </summary>
protected void Seek(ref int a, ref int b, int ainc, int binc, Func<int, int, bool> f) {
a += ainc;
b += binc;
if (f(a, b)) {
int weight = 1;
do {
weight *= 2;
a += ainc * weight;
b += binc * weight;
}
while (f(a, b));
do {
weight /= 2;
int adec = ainc * weight;
int bdec = binc * weight;
if (!f(a - adec, b - bdec)) {
a -= adec;
b -= bdec;
}
}
while (weight > 1);
}
}
}