C# How to stop decimal shift on multiple times through - c#

I have created a form to arrange a ship hold.
Problem: On multiple times through, it comes back with the decimal point shifted two more spaces. How do I keep it from shifting further?
Code Snippet:
private void btnGetInv_Click(object sender, EventArgs e)
{
//if this is a ship hold, roll percentile to get how much of the ship's total weight capacity is being carried.
if(ShipHold==true)
{
decimal Percentage = 0;
decimal HoldWeight = 0;
LoNum = 1;
HiNum = 100;
DieResult = 0;
Percentage = DieResult + 1 + RollDice.Next(LoNum - 1, HiNum);
WeightAvail = Percentage / 100 * WeightAvail;
HoldWeight = WeightAvail;
RTBItems.Text = "percentage of weight = " + HoldWeight + "\r\n" + RTBItems.Text;
}
WeightAvail is also a decimal. However; LoNum, HiNum, and DieResult are all types int to work with the Random.Next function. It might be best if I can round to the nearest whole number. I'm using Visual Studio 2019.
This was my fix:
HoldWeight = Percentage / 100 * WeightAvail;
//HoldWeight = WeightAvail;
RTBItems.Text = "percentage of weight = " + HoldWeight + "\r\n" + RTBItems.Text;
HoldWeight = 0;

Related

Amortisation schedule, monthly principal decreasing instead of increasing

Hi I am trying to create a amortization schedule , which shows the EMI , Principal , Interest and the new Principal for next month. The problem is that the monthly principal instead of increasing keeps on decreasing. As far as from searching the net , I am doing the right calculations. What am I missing?
decimal principal = 312500;
decimal rate = 3.50M;
decimal EMI;
decimal monthlyInterest;
decimal monthlyPrincipal;
decimal newPrincipalBalance;
decimal downPayment = 62500;
decimal actualPrincipal = principal - downPayment;
for (int i = 0; i <= 24; i++)
{
Console.WriteLine("principal " + actualPrincipal);
EMI = Math.Round(monthlyPayments(actualPrincipal, rate, 30));
Console.WriteLine("EMI " + EMI);
monthlyInterest = actualPrincipal * rate / 12;
monthlyInterest = Math.Round((actualPrincipal * rate / 100) / 12);
Console.WriteLine("monthlyInterest " + monthlyInterest);
monthlyPrincipal = Math.Round(EMI - monthlyInterest);
Console.WriteLine("monthlyPrincipal " + monthlyPrincipal);
newPrincipalBalance = Math.Round(actualPrincipal - monthlyPrincipal);
Console.WriteLine("newPrincipalBalance " + newPrincipalBalance);
Console.WriteLine("===================================");
actualPrincipal = newPrincipalBalance;
}
}
public static decimal monthlyPayments(decimal actualPrincipal, decimal rate, int years)
{
rate = rate / 1200;
years = years * 12;
decimal F = (decimal)Math.Pow((double)(1 + rate), years);
return actualPrincipal * (rate * F) / (F - 1);
}
These are first few of my results where the monthly principal is decreasing
This is what the expected result is :
This is the formula for calculating the monthlyPayments
I found what I was doing wrong. As the EMI remains fixed for the entire period of the loan, it should have been kept outside the loop.
Console.WriteLine("principal " + actualPrincipal);
EMI = Math.Round(monthlyPayments(actualPrincipal, rate, 30));
Console.WriteLine("EMI " + EMI);
for (int i = 0; i <= 24; i++)
{
monthlyInterest = actualPrincipal * rate / 12;
monthlyInterest = Math.Round((actualPrincipal * rate / 100) / 12);
Console.WriteLine("monthlyInterest " + monthlyInterest);
monthlyPrincipal = Math.Round(EMI - monthlyInterest);
Console.WriteLine("monthlyPrincipal " + monthlyPrincipal);
newPrincipalBalance = Math.Round(actualPrincipal - monthlyPrincipal);
Console.WriteLine("newPrincipalBalance " + newPrincipalBalance);
Console.WriteLine("===================================");
actualPrincipal = newPrincipalBalance;
}

C# WinsForm, Frequency Distribution Table [Updated]

Update 01
Thanks to Caius, found the main problem, the logic on the "if" was wrong, now fixed and giving the correct results. The loop still create more positions than needed on the secondary List, an extra position for each number on the main List.
I've updated the code bellow for refence for the following question:
-001 I can figure out why it create positions that needed, the for loop should run only after the foreach finishes its loops correct?
-002 To kind of solving this issue, I've used a List.Remove() to remove all the 0's, so far no crashes, but, the fact that I'm creating the extra indexes, and than removing them, does means a big performance down if I have large list of numbers? Or is an acceptable solution?
Description
It supposed to read all numbers in a central List1 (numberList), and count how many numbers are inside a certain (0|-15 / 15|-20) range, for that I use another List, that each range is a position on the List2 (numberSubList), where each number on List2, tells how many numbers exists inside that range.
-The range changes as the numbers grows or decrease
Code:
void Frequency()
{
int minNumb = numberList.Min();
int maxNumb = numberList.Max();
int size = numberList.Count();
numberSubList.Clear();
dGrdVFrequency.Rows.Clear();
dGrdVFrequency.Refresh();
double k = (1 + 3.3 * Math.Log10(size));
double h = (maxNumb - minNumb) / k;
lblH.Text = $"H: {Math.Round(h, 2)} / Rounded = {Math.Round(h / 5) * 5}";
lblK.Text = $"K: {Math.Round(k, 4)}";
if (h <= 5) { h = 5; }
else { h = Math.Round(h / 5) * 5; }
int counter = 1;
for (int i = 0; i < size; i++)
{
numberSubList.Add(0); // 001 HERE, creating more positions than needed, each per number.
foreach (int number in numberList)
{
if (number >= (h * i) + minNumb && number < (h * (i + 1)) + minNumb)
{
numberSubList[i] = counter++;
}
}
numberSubList.Remove(0); // 002-This to remove all the extra 0's that are created.
counter = 1;
}
txtBoxSubNum.Clear();
foreach (int number in numberSubList)
{
txtBoxSubNum.AppendText($"{number.ToString()} , ");
}
lblSubTotalIndex.Text = $"Total in List: {numberSubList.Count()}";
lblSubSumIndex.Text = $"Sum of List: {numberSubList.Sum()}";
int inc = 0;
int sum = 0;
foreach (int number in numberSubList)
{
sum = sum + number;
int n = dGrdVFrequency.Rows.Add();
dGrdVFrequency.Rows[n].Cells[0].Value = $"{(h * inc) + minNumb} |- {(h * (1 + inc)) + minNumb}";
dGrdVFrequency.Rows[n].Cells[1].Value = $"{number}";
dGrdVFrequency.Rows[n].Cells[2].Value = $"{sum}";
dGrdVFrequency.Rows[n].Cells[3].Value = $"{(number * 100) / size} %";
dGrdVFrequency.Rows[n].Cells[4].Value = $"{(sum * 100) / size} %";
inc++;
}
}
Screen shot showing the updated version.
I think, if your aim is to only store eg 17 in the "15 to 25" slot, this is wonky:
if (number <= (h * i) + minNumb) // Check if number is smaller than the range limit
Because it's found inside a loop that will move on to the next range, "25 to 35" and it only asks if the number 17 is less than the upper limit (and 17 is less than 35) so 17 is accorded to the 25-35 range too
FWIW the range a number should be in can be derived from the number, with (number - min) / number_of_ranges - at the moment you create your eg 10 ranges and then you visit each number 10 times looking to put it in a range, so you do 9 times more operations than you really need to

Loop to calculate organism growth

I am trying to make a loop that calculates number of organisms over time, but I am stuck on how to make the loop update. Do I need to put something outside the for loop to update the total organisms?
private void calculateButton_Click(object sender, EventArgs e)
{
//declare variables for number of days passed and population
double days;
double organisms;
double increaseDaily;
double total_organisms;
//declare the constants to be used
const int interval = 1;
const int start_days = 1;
//try parse to get amount of starting organisms
if (double.TryParse(organismTextBox.Text, out organisms))
{
//try parse to get the percent daily increase
if (double.TryParse(dailyIncreaseTextBox.Text, out increaseDaily))
{
//try parse to get the number of days passed
if (double.TryParse(daysMultiplyTextBox.Text, out days))
{
//for loop to count through the number of days
for (int i = 1; i <= days; i += interval)
{
//calculate the amount of organisms
total_organisms = (organisms * (increaseDaily / 100) + organisms);
//display the amount of organisms after an amount of time
listBox1.Items.Add("after " + i + " days, the amount of organisms is " + total_organisms);
}
Each loop, you are calculating the total_organisms as the sum of organisms plus some percent:
total_organisms = (organisms * (increaseDaily / 100) + organisms);
You are never changing the value of organisms, so total_organisms will be calculated as the same value each loop. You should just updated the value of organisms instead.
Also, you could reduce indentation in your code by changing each if statement to test for parse failure and bail out:
private void calculateButton_Click(object sender, EventArgs e)
{
//declare variables for number of days passed and population
double days;
double organisms;
double increaseDaily;
List<string> errors = new List<string>();
//declare the constants to be used
const int interval = 1;
const int start_days = 1;
//try parse to get amount of starting organisms
if (!double.TryParse(organismTextBox.Text, out organisms)) {
errors.Add("Organisms must be a valid number");
}
//try parse to get the percent daily increase
if (double.TryParse(dailyIncreaseTextBox.Text, out increaseDaily)) {
errors.Add("Daily increase must be a valid number");
}
//try parse to get the number of days passed
if (double.TryParse(daysMultiplyTextBox.Text, out days)) {
errors.Add("Number of days must be a valid number");
}
if (errors.Any()) {
// Display errors to user here (depending on your UI)
return;
}
//for loop to count through the number of days
for (int i = 1; i <= days; i += interval) {
//calculate the amount of organisms
organisms = (organisms * (increaseDaily / 100) + organisms);
//display the amount of organisms after an amount of time
listBox1.Items.Add(
"after " + i + " days, the amount of organisms is " + organisms);
}
}
If you want to keep adding to the total number of organisms:
total_organisms = total_organisms + (organisms * (increaseDaily / 100) + organisms);

Averaging increasing number of variables

I have to report average value of incoming numbers, how i could do that without using some sort of data structure to keep track of all values and then calculating average by summing them and dividing by number of values?
Just keep running sum and how many numbers you have received, that's all you need to compute the average.
If I'm not totally mistaken, one could calculate the avg(n+1) also this way:
avg(n+1) = (a[1]+ ... + a[n+1]) / (n+1) =
= (a[1]+ ... + a[n])/(n+1) + a[n+1]/(n+1) =
= (n(a[1]+ ... + a[n])/n) / (n+1) + a[n+1]/(n+1) =
= n*avg(n) / (n+1) + a[n+1]/(n+1) =
= n/(n+1) * avg(n) + a[n+1]/(n+1)
so multiply the old avg by n/(n+1) and add the new element divided by n+1. Depending on how high n will get and how big your values are, this could reduce rounding errors...
EDIT: Of course you have to calculate n/(n+1) using floats, otherwise it will always render 0...
If you have the numbers a[1] a[2] ... a[n] and you know their average is avg(n) = (a[1] + ... + a[n]) / n, then when you get another number a[n + 1] you can do:
avg(n + 1) = (avg(n) * n + a[n + 1]) / (n + 1)
Some floating point errors are unavoidable, but you should test this and see if it's good enough.
To avoid overflow, you could do the division first:
avg(n + 1) = (avg(n) / (n + 1)) * n + (a[n + 1] / (n + 1))
Keep the current sum and count. Update both on every incoming number.
avg = sum / count.
you don't need to keep track the total sum, only counter:
class Averager {
float currentAverage;
size_t count;
float addData (float value) {
this->currentAverage += (value - this->currentAverage) / ++count;
return this->currentAverage;
}
}
from-> prevent long running averaging from overflow?

C# is there a problem with division?

This is a piece of my code, it is called every second, after about 10 seconds the values start to become weird (see below):
double a;
double b;
for (int i = 0; i < currAC.Length; i++ )
{
a = currAC[i];
b = aveACValues[i];
divisor = (a/b);
Console.WriteLine("a = " + a.ToString("N2") + "\t" + "b = " + b.ToString("N2"));
Console.WriteLine("divisor = " + divisor);
Console.WriteLine("a+b = " + (a+b));
}
and the output:
a = -0.05 b = 0.00
divisor = 41
a+b = -0.0524010372273268
currAC and aveACValues are double[]
what on earth is going on???? The addition result is correct every time, but the division value is wrong, yet it is reading a and b correctly??
EDIT: '41' is the value of the first calculation, ie when a = currAC[0], but this should not remain???
if b == -0.001219512195122, then a/b==41, and a+b==-0.051219512195122 - so something around those areas (rounding etc) sounds feasible...
Also; note that for some arithmetic, it is possible it is using values that are still in registers. Registers may exhibit slightly different accuracy (and so give different results) than local variables.
double can result in inprecise results, due to the specification (see msdn). You might want to use decimal instead, it offers more precision.
What happens if the variables are declared as close to where they are used as is possible?
for (int i = 0; i < currAC.Length; i++ )
{
double a = currAC[i];
double b = aveACValues[i];
double divisor = (a/b);
Console.WriteLine("a = " + a.ToString("N2") + "\t" + "b = " + b.ToString("N2"));
Console.WriteLine("divisor = " + divisor.ToString());
Console.WriteLine("a+b = " + (a+b).ToString());
}
This would ensure that you have a fresh "divisor" each time and will not be affected by other scopes.

Categories