Calculating RSI for new elements optimization - c#

The following formula would calculate RSI for all prices. Whenever I add a new price to the array, it would have to recalculate the RSI for the entire array/all prices. Is there a way I can calculate the RSI for the new price without having to recalculate the entire thing? I want to do that because when I'm calculating this for every new candle and it has to be executed for the least possible time. If it's not possible, can you please suggest me a way to optimize it, so it executes as fast as possible. Right now, it's not so fast.
decimal[] prices = ...
Calculate(prices, 14);
public decimal[] Calculate(decimal[] price, int period)
{
var rsi = new decimal[price.Length];
decimal gain = 0m;
decimal loss = 0m;
rsi[0] = 0m;
for (int i = 1; i <= period; ++i)
{
var diff = price[i] - price[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 < price.Length; ++i)
{
var diff = price[i] - price[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;
}

Related

Calculate the ticks of an axis for a chart with a stepsize

I've calculated a stepsize for an axis on a chart.
Also I have the Min and Max -Values. Now I need to calculate all ticks so that all values between my Min and Max can be displayed.
For Example:
Stepsize: 1000
Min: 213
Max: 4405
Expected ticks: 0,1000,2000,3000,4000,5000
Stepsize: 500
Min: -1213
Max: 1405
Expected ticks: -1500,-1000,-500,0,500,1000,1500
Until now I'm trying to calculate the first value with "try and error" like:
bool firstStepSet = false;
double firstStep = stepSize;
do
{
if (xValue >= (firstStep - (stepSize / 2)) && xValue <=
(firstStep + (stepSize / 2)))
{
firstStepSet = true;
this.myBarXValues.Add(firstStep, 0);
}
else if (xValue > stepSize)
{
firstStep += stepSize;
}
else
{
firstStep -= stepSize;
}
}
while (!firstStepSet);
And after that I'm adding steps to this list until all values fit.
This seems pretty dirty to me and I want to know if there is another solution.
So what I need is a solution which calculate the first tick that I need.
This function calculates first and last step values:
static void CalcSteps(int min, int max, int stepSize, out int firstStep, out int lastStep)
{
if (min >= 0)
{
firstStep = (min / stepSize) * stepSize;
}
else
{
firstStep = ((min - stepSize + 1) / stepSize) * stepSize;
}
if (max >= 0)
{
lastStep = ((max + stepSize - 1) / stepSize) * stepSize;
}
else
{
lastStep = (max / stepSize) * stepSize;
}
}
You can calculate axis limits using integer rounding to lower and higher values
low = stepsize * (min / stepsize) //integer division needed
high = stepsize * ((max + stepsize - 1) / stepsize)
Example Python code returns limits and number of ticks (one more than interval count)
def getminmax(minn, maxx, step):
low = (minn // step)
high = (maxx + step - 1) // step
ticks = high - low + 1
return low * step, high * step, ticks
print(getminmax(213, 4405, 1000))
print(getminmax(-1213,1405, 500))
(0, 5000, 6)
(-1500, 1500, 7)

Relative Strength Index

I'm trying to calculate the relative strength index, RSI, for a financial instrument. When I compare my calculation with one done by a commercial software, they don't look the same. I can't figure out what I'm doing wrong. Can anyone help?
The RSI formula:
{100} - (100/(1+RS)). Where RS is AvgGain(N periods)/AvgLoss( N periods)
public DataTable RSI(string instrument, int period, string oper, int entryVal)
{
DataTable dtRSI = new DataTable(); //table to return
dtRSI.Columns.Add("Date");
dtRSI.Columns.Add("Instrument");
dtRSI.Columns.Add("Close");
dtRSI.Columns.Add("RSI");
//Load Datatable from database
DataTable dt = new DataTable();
dt = conn.ExtractDataFromDb(instrument);
int column = 1; //Close price
//variables to RSI formula
Queue<float> avgUp = new Queue<float>();
Queue<float> avgDown = new Queue<float>();
float close1, close2, rsi, rs;
float avgUp1, avgUp2, newAvgUp, avgDown1, avgDown2, newAvgDown;
string[] dateCloseRsi = new string[3]; //row of data to insert into new table
string date; //date of calculation
string[] splitDate = new string[2];
//get first close
close1 = float.Parse(dt.Rows[0][column].ToString());
dt.Rows.RemoveAt(0);
//get close for number of periods into the que-list
for (int i = 1; i <= period; i++)
{
close2 = float.Parse(dt.Rows[0][column].ToString());
//are todays close higher then yesterday?
if (close2 > close1)
{
avgUp.Enqueue(close2 - close1);
avgDown.Enqueue(0);
}
else if (close2<close1)
{
avgUp.Enqueue(0);
avgDown.Enqueue(close1 - close2);
}
else
{
avgUp.Enqueue(0);
avgDown.Enqueue(0);
}
close1 = close2;
dt.Rows.RemoveAt(0);
}
//iterate datatable and calculate RSI
foreach (DataRow rows in dt.Rows)
{
avgUp1 = float.Parse(avgUp.Average().ToString("n2")); //calculate yesterdays avg difference on up days
avgDown1 = float.Parse(avgDown.Average().ToString("n2")); //calculate yesterdays avg difference on down days
avgUp.Dequeue();
avgDown.Dequeue();
close2 = float.Parse(rows[column].ToString()); //todays close
//close today higher then yesterday?
if (close2 > close1)
{
avgUp.Enqueue(close2 - close1);
avgDown.Enqueue(0);
}
else if (close2 < close1)
{
avgDown.Enqueue(close1 - close2);
avgUp.Enqueue(0);
}
else
{
avgUp.Enqueue(0);
avgDown.Enqueue(0);
}
avgUp2 = float.Parse(avgUp.Average().ToString("n2")); //todays avg difference on up days
avgDown2 = float.Parse(avgDown.Average().ToString("n2")); //todays avg difference on down days
newAvgUp = ((avgUp1 * (period - 1)) + avgUp2) / period; //yesterdays and todays avg diff value on up days
newAvgDown = ((avgDown1 * (period - 1)) + avgDown2) / period; //yesterdays and todays avg diff value on down days
newAvgUp = float.Parse(newAvgUp.ToString("n2")); //round to 2 decimals
newAvgDown = float.Parse(newAvgDown.ToString("n2")); //round to 2 decimals
rs = newAvgUp / newAvgDown; //calc Relative Strength
rs = float.Parse(rs.ToString("n2")); //round to 2 decimals
rsi = 100 - (100 / (1 + rs)); //Calc RSI
rsi = float.Parse(rsi.ToString("n2")); //round to 2 decimals
close1 = close2; //todays close become yesterdays close for tomorrow
//remove time from date
date = rows[0].ToString();
splitDate = date.Split(' ');
date = splitDate[0];
//add data to dtRSI
DataRow rsiRow = dtRSI.NewRow();
rsiRow["Date"] = date;
rsiRow["Instrument"] = instrument;
rsiRow["Close"] = rows[column];
rsiRow["RSI"] = rsi;
dtRSI.Rows.Add(rsiRow);
}
return dtRSI; //returns a table with Date, Instrument, Close Price and RSI
}
Hello, here's a tested and verified C# class which generates RSI values with 100% accuracy:
using System;
using System.Data;
using System.Globalization;
namespace YourNameSpace
{
class PriceEngine
{
public static DataTable data;
public static double[] positiveChanges;
public static double[] negativeChanges;
public static double[] averageGain;
public static double[] averageLoss;
public static double[] rsi;
public static double CalculateDifference(double current_price, double previous_price)
{
return current_price - previous_price;
}
public static double CalculatePositiveChange(double difference)
{
return difference > 0 ? difference : 0;
}
public static double CalculateNegativeChange(double difference)
{
return difference < 0 ? difference * -1 : 0;
}
public static void CalculateRSI(int rsi_period, int price_index = 5)
{
for(int i = 0; i < PriceEngine.data.Rows.Count; i++)
{
double current_difference = 0.0;
if (i > 0)
{
double previous_close = Convert.ToDouble(PriceEngine.data.Rows[i-1].Field<string>(price_index));
double current_close = Convert.ToDouble(PriceEngine.data.Rows[i].Field<string>(price_index));
current_difference = CalculateDifference(current_close, previous_close);
}
PriceEngine.positiveChanges[i] = CalculatePositiveChange(current_difference);
PriceEngine.negativeChanges[i] = CalculateNegativeChange(current_difference);
if(i == Math.Max(1,rsi_period))
{
double gain_sum = 0.0;
double loss_sum = 0.0;
for(int x = Math.Max(1,rsi_period); x > 0; x--)
{
gain_sum += PriceEngine.positiveChanges[x];
loss_sum += PriceEngine.negativeChanges[x];
}
PriceEngine.averageGain[i] = gain_sum / Math.Max(1,rsi_period);
PriceEngine.averageLoss[i] = loss_sum / Math.Max(1,rsi_period);
}else if (i > Math.Max(1,rsi_period))
{
PriceEngine.averageGain[i] = ( PriceEngine.averageGain[i-1]*(rsi_period-1) + PriceEngine.positiveChanges[i]) / Math.Max(1, rsi_period);
PriceEngine.averageLoss[i] = ( PriceEngine.averageLoss[i-1]*(rsi_period-1) + PriceEngine.negativeChanges[i]) / Math.Max(1, rsi_period);
PriceEngine.rsi[i] = PriceEngine.averageLoss[i] == 0 ? 100 : PriceEngine.averageGain[i] == 0 ? 0 : Math.Round(100 - (100 / (1 + PriceEngine.averageGain[i] / PriceEngine.averageLoss[i])), 5);
}
}
}
public static void Launch()
{
PriceEngine.data = new DataTable();
//load {date, time, open, high, low, close} values in PriceEngine.data (6th column (index #5) = close price) here
positiveChanges = new double[PriceEngine.data.Rows.Count];
negativeChanges = new double[PriceEngine.data.Rows.Count];
averageGain = new double[PriceEngine.data.Rows.Count];
averageLoss = new double[PriceEngine.data.Rows.Count];
rsi = new double[PriceEngine.data.Rows.Count];
CalculateRSI(14);
}
}
}
For detailed step-by-step instructions, I wrote a lengthy article, you can check it here:
https://turmanauli.medium.com/a-step-by-step-guide-for-calculating-reliable-rsi-values-programmatically-a6a604a06b77
P.S. You need global variables to store prevous values, this is not an option for indicators like RSI, simple function work only for simple indicators like Simple Moving Average. All smoothed / Weighted indicators need buffers / global arrays to store data.

RSI vs Wilder's RSI Calculation Problems

I am having trouble getting a smoothed RSI. The below picture is from freestockcharts.com. The calculation uses this code.
public static double CalculateRsi(IEnumerable<double> closePrices)
{
var prices = closePrices as double[] ?? closePrices.ToArray();
double sumGain = 0;
double sumLoss = 0;
for (int i = 1; i < prices.Length; i++)
{
var difference = prices[i] - prices[i - 1];
if (difference >= 0)
{
sumGain += difference;
}
else
{
sumLoss -= difference;
}
}
if (sumGain == 0) return 0;
if (Math.Abs(sumLoss) < Tolerance) return 100;
var relativeStrength = sumGain / sumLoss;
return 100.0 - (100.0 / (1 + relativeStrength));
}
https://stackoverflow.com/questions/...th-index-using-some-programming-language-js-c
This seems to be the pure RSI with no smoothing. How does a smoothed RSI get calculated? I have tried changing it to fit the definitions of the these two sites however the output was not correct. It was barely smoothed.
(I don't have enough rep to post links)
tc2000 -> Indicators -> RSI_and_Wilder_s_RSI (Wilder's smoothing = Previous MA value + (1/n periods * (Close - Previous MA)))
priceactionlab -> wilders-cutlers-and-harris-relative-strength-index (RS = EMA(Gain(n), n)/EMA(Loss(n), n))
Can someone actually do the calculation with some sample data?
Wilder's RSI vs RSI
In order to calculate the RSI, you need a period to calculate it with. As noted on Wikipedia, 14 is used quite often.
So the calculation steps would be as follows:
Period 1 - 13, RSI = 0
Period 14:
AverageGain = TotalGain / PeriodCount;
AverageLoss = TotalLoss / PeriodCount;
RS = AverageGain / AverageLoss;
RSI = 100 - 100 / (1 + RS);
Period 15 - to period (N):
if (Period(N)Change > 0
AverageGain(N) = ((AverageGain(N - 1) * (PeriodCount - 1)) + Period(N)Change) / PeriodCount;
else
AverageGain(N) = (AverageGain(N - 1) * (PeriodCount - 1)) / PeriodCount;
if (this.Change < 0)
AverageLoss(N) = ((AverageLoss(N - 1) * (PeriodCount - 1)) + Math.Abs(Period(N)Change)) / PeriodCount;
else
AverageLoss(N) = (AverageLoss(N - 1) * (PeriodCount - 1)) / PeriodCount;
RS = AverageGain / AverageLoss;
RSI = 100 - (100 / (1 + RS));
Thereafter, to smooth the values, you need to apply a moving average of a certain period to your RSI values. To do that, traverse your RSI values from the last index to the first and calculate your average for the current period based on the preceding x smoothing periods.
Once done, just reverse the list of values to get the expected order:
List<double> SmoothedRSI(IEnumerable<double> rsiValues, int smoothingPeriod)
{
if (rsiValues.Count() <= smoothingPeriod)
throw new Exception("Smoothing period too large or too few RSI values passed.");
List<double> results = new List<double>();
List<double> reversedRSIValues = rsiValues.Reverse().ToList();
for (int i = 1; i < reversedRSIValues.Count() - smoothingPeriod - 1; i++)
results.Add(reversedRSIValues.Subset(i, i + smoothingPeriod).Average());
return results.Reverse().ToList();
}
The Subset method is just a simple extension method as follows:
public static List<double> Subset(this List<double> values, int start, int end)
{
List<double> results = new List<double>();
for (int i = start; i <= end; i++)
results.Add(values[i]);
return results;
}
Disclaimer, I did not test the code, but it should give you an idea of how the smoothing is applied.
You can't get accurate values without buffers / global variables to store data.
This is a smoothed indicator, meaning it doesn't only use 14 bars but ALL THE BARS:
Here's a step by step article with working and verified source codes generating exactly the same values if prices and number of available bars are the same, of course (you only need to load the price data from your source):
Tested and verified:
using System;
using System.Data;
using System.Globalization;
namespace YourNameSpace
{
class PriceEngine
{
public static DataTable data;
public static double[] positiveChanges;
public static double[] negativeChanges;
public static double[] averageGain;
public static double[] averageLoss;
public static double[] rsi;
public static double CalculateDifference(double current_price, double previous_price)
{
return current_price - previous_price;
}
public static double CalculatePositiveChange(double difference)
{
return difference > 0 ? difference : 0;
}
public static double CalculateNegativeChange(double difference)
{
return difference < 0 ? difference * -1 : 0;
}
public static void CalculateRSI(int rsi_period, int price_index = 5)
{
for(int i = 0; i < PriceEngine.data.Rows.Count; i++)
{
double current_difference = 0.0;
if (i > 0)
{
double previous_close = Convert.ToDouble(PriceEngine.data.Rows[i-1].Field<string>(price_index));
double current_close = Convert.ToDouble(PriceEngine.data.Rows[i].Field<string>(price_index));
current_difference = CalculateDifference(current_close, previous_close);
}
PriceEngine.positiveChanges[i] = CalculatePositiveChange(current_difference);
PriceEngine.negativeChanges[i] = CalculateNegativeChange(current_difference);
if(i == Math.Max(1,rsi_period))
{
double gain_sum = 0.0;
double loss_sum = 0.0;
for(int x = Math.Max(1,rsi_period); x > 0; x--)
{
gain_sum += PriceEngine.positiveChanges[x];
loss_sum += PriceEngine.negativeChanges[x];
}
PriceEngine.averageGain[i] = gain_sum / Math.Max(1,rsi_period);
PriceEngine.averageLoss[i] = loss_sum / Math.Max(1,rsi_period);
}else if (i > Math.Max(1,rsi_period))
{
PriceEngine.averageGain[i] = ( PriceEngine.averageGain[i-1]*(rsi_period-1) + PriceEngine.positiveChanges[i]) / Math.Max(1, rsi_period);
PriceEngine.averageLoss[i] = ( PriceEngine.averageLoss[i-1]*(rsi_period-1) + PriceEngine.negativeChanges[i]) / Math.Max(1, rsi_period);
PriceEngine.rsi[i] = PriceEngine.averageLoss[i] == 0 ? 100 : PriceEngine.averageGain[i] == 0 ? 0 : Math.Round(100 - (100 / (1 + PriceEngine.averageGain[i] / PriceEngine.averageLoss[i])), 5);
}
}
}
public static void Launch()
{
PriceEngine.data = new DataTable();
//load {date, time, open, high, low, close} values in PriceEngine.data (6th column (index #5) = close price) here
positiveChanges = new double[PriceEngine.data.Rows.Count];
negativeChanges = new double[PriceEngine.data.Rows.Count];
averageGain = new double[PriceEngine.data.Rows.Count];
averageLoss = new double[PriceEngine.data.Rows.Count];
rsi = new double[PriceEngine.data.Rows.Count];
CalculateRSI(14);
}
}
}
For detailed step-by-step instructions, I wrote a lengthy article, you can check it here: https://turmanauli.medium.com/a-step-by-step-guide-for-calculating-reliable-rsi-values-programmatically-a6a604a06b77
P.S. functions only work for simple indicators (Simple Moving Average), even Exponential Moving Average, Average True Range absolutely require global variables to store previous values.

How to Improve runtime for Iterative Function

My codes runtime is getting quite long, it has to iterate maybe 30,000 times and that takes around 30 sec on my computer but longer on the clients. I'm wondering if I can get any tips to speed up the calculations. The iterative functions has to put the value as close to zero as possible and it decreases or increases until the conditions are met. I sped it up by increasing by $1 then when it gets closer to the range it increments by $0.1 for precision.
I Broke it into nodes. There's an initial node, then it checks difference of left node then difference of right node. Which ever produces a smaller difference it goes in that direction until difference is as small as possible.
//Current Node
double InitialFinalPayment = bruteforceFlexibleAmortization(0);
double intialPaymentAmount = Calc_Payment(Findper() - FindSkipNMonths(), Findnint(),
Findpv() + InterestPaid, Findfv(), Findcf(), Findpf(), Finddisc(),
Findbep()), 2);
double diffInitial = Math.Abs(intialPaymentAmount - InitialFinalPayment);
decimal runningIncrement = 1M;
double nextPayment = 0;
//Node Ahead
Double incrementOutcome = bruteforceFlexibleAmortization(runningIncrement);
Double incrementPayment = intialPaymentAmount + (double)runningIncrement;
Double diffincrement = Math.Abs(incrementPayment - incrementOutcome);
//Node Behind
Double decrementOutcome = bruteforceFlexibleAmortization(-runningIncrement);
Double decrementPayment = intialPaymentAmount - (double)runningIncrement;
Double diffdecrement = Math.Abs(decrementPayment - decrementOutcome);
if (diffincrement < diffInitial)
{
runningIncrement += 1.0M;
double nextValue = bruteforceFlexibleAmortization(runningIncrement);
nextPayment = intialPaymentAmount + (double)runningIncrement;
double diffNext = Math.Abs(nextPayment - nextValue);
while (diffNext < diffdecrement)
{
diffdecrement = diffNext;
runningIncrement += 1.0M;
nextValue = bruteforceFlexibleAmortization(runningIncrement);
nextPayment = intialPaymentAmount + (double)runningIncrement;
diffNext = Math.Abs(nextPayment - nextValue);
}
diffincrement = diffNext;
runningIncrement -= 0.01M;
nextValue = bruteforceFlexibleAmortization(runningIncrement);
nextPayment = intialPaymentAmount + (double)runningIncrement;
diffNext = Math.Abs(nextPayment - nextValue);
while (diffNext < diffincrement)
{
diffincrement = diffNext;
runningIncrement -= 0.01M;
nextValue = bruteforceFlexibleAmortization(runningIncrement);
nextPayment = intialPaymentAmount + (double)runningIncrement;
diffNext = Math.Abs(nextPayment - nextValue);
}
return nextPayment + (double)0.01M;
}
else if (diffdecrement < diffInitial)
{
runningIncrement += 1.0M;
double nextValue = bruteforceFlexibleAmortization(-runningIncrement);
nextPayment = intialPaymentAmount - (double)runningIncrement;
double diffNext = Math.Abs(nextPayment - nextValue);
while (diffNext < diffdecrement)
{
diffdecrement = diffNext;
runningIncrement += 1.0M;
nextValue = bruteforceFlexibleAmortization(-runningIncrement);
nextPayment = intialPaymentAmount - (double)runningIncrement;
diffNext = Math.Abs(nextPayment - nextValue);
}
diffincrement = diffNext;
runningIncrement -= 0.01M;
nextValue = bruteforceFlexibleAmortization(-runningIncrement);
nextPayment = intialPaymentAmount - (double)runningIncrement;
diffNext = Math.Abs(nextPayment - nextValue);
while (diffNext < diffincrement)
{
diffincrement = diffNext;
runningIncrement -= 0.01M;
nextValue = bruteforceFlexibleAmortization(-runningIncrement);
nextPayment = intialPaymentAmount - (double)runningIncrement;
diffNext = Math.Abs(nextPayment - nextValue);
}
return nextPayment - (double)0.01M;
}
return InitialFinalPayment;
}
The only idea I have is by increasing/decreasing running increment to be a larger value and making that value smaller as it gets closer to the value. Like how it's 1 then 0.1, maybe 10 then 1 then 0.1 but it's definitely not gonna be clean code
If your bruteforceFlexibleAmortization is monotonic you should try using similar approach that's used by binary search instead of changing your input by the same value all the time.
var currentValue = 50;
var lastLower = 0;
var lastGreater = 100;
do
{
var currentResult = calculateResult(initialValue);
if(currentResult < expectedResult)
{
lastLower = currentValue;
currentValue = (currentValue + lastGreater) / 2;
}
else
{
lastGreater = currentValue;
currentValue = (currentValue + lastLower) / 2;
}
} while (Math.Abs(currentResult - expectedResult) < epsilon)
It will be a little different for you, because you need to call 2 methods, but you should get the point.
e.g. If you're trying to interpolate square root for a given n to a certain precision using your approach would be really slow. With binary-like you can get much more iterations to get close interpolation.

Round any n-digit number to (n-1) zero-digits

Sorry hard to formulate.
I need to round like this:
12 -> 10
152 -> 200
1538 -> 2000
25000 -> 30000
etc.
Twisting my head, but can't see how to make this. Must work for any n number of digits. Anyone got an elegant method for it?
c# or vb.net
How about this:
double num = 152;
int pow = (int)Math.Log10(num);
int factor = (int)Math.Pow(10, pow);
double temp = num / factor;
double result = Math.Round(temp) * factor;
I think you should try with something like this:
public int Round( int number)
{
int power = number.ToString().Length - 1;
int sz = Math.Pow(10, power);
int rounded = (int)Math.Round( number / sz );
return rounded * sz;
}
The idea is to get the size of the nearest 10 power, available by the length of the number expressed as a string. Then divide the number by that power, leaving it like 1,2 and then round it using the Math.Round method and restore the size by remultiplying it to the power.
Much like the previous answer...
I would do it this way:
double d = 25000;
int power = d.ToString().Length - 1;
double multipler = Math.Pow(10,power);
d = Math.Round(d / multipler) * multipler;
Console.WriteLine(d);
One of the way could be
Convert the number to Decimal
Divide it by 10^(n-1) (where n is number of digits)
Now use round function (Decimal.Round)
Multiply again by 10^(n-1)
Divide the number by 10n and round the result, then multiply the result back with 10n;
int MyRound(int num)
{
double divisor = Math.Pow(10, num.ToString().Length - 1);
return (int)(Math.Round(num / divisor, MidpointRounding.AwayFromZero) * divisor);
}
Note that we should use MidpointRounding.AwayFromZero when rounding because of the default banker's rounding.
int MakeOneSigFig(int value)
{
int neg = 1;
if(value <= 10 && value >= -10) { return value; }
if(value == int.MinValue) { value = int.MaxValue; neg = -1; }
if(value < 0) { value = -value; neg = -1; }
int mult = 10; // start at 10 because we've got 'firstDigit = value / 10' below
while(value > 99) { value /= 10; mult *= 10; }
int firstDigit = value / 10;
if(value % 10 >= 5) firstDigit++;
return neg * firstDigit * mult;
}
This is equivalent to MidpointRounding.AwayFromZero. This method doesn't do any double math or string conversions. If you didn't want to loop, you could replace that with the if block below. That would be more efficient, but more code and not quite as easy to read.
if(value < 100) { mult = 10; }
else if(value < 1000) { mult = 100; value /= 10; }
else if(value < 10000) { mult = 1000; value /= 100; }
else if(value < 100000) { mult = 10000; value /= 1000; }
// etc.

Categories

Resources