Overcoming a discrepancy in a division - c#

I have the following code to divide an amount by a number and allocate the result as an amount that needs to be paid per month.
objData.month_per_amount = (Convert.ToDecimal(txtAmount.Value) / Convert.ToInt32(txtMonths.Value));
In a scenario example if I divide 13 by 3 and round off the result to 2 decimal places I get 4.33 for each month. But when I multiply 4.33 by 3 I am getting 12.99, which is not equivalent to 13. There is a discrepancy of 0.01. In this scenario how can I allocate like below:
month 1: 4.33
month 2: 4.33
month 3: 4.34
Hope I made it clear, the preferred code should only be executed if there is such a discrepancy, for example if 14 is to be divided by 2, we get 7 for each month and 7+7=14, so exactly the same figure we are getting here.

In accounting you'd often use something called 'reducing balance' for this. The idea is that you calculate the month's total, deduct it from the overall total and reduce the number of months. So something like:
decimal balance = 13m;
int months = 3;
int monthsRemaining = 3;
for (var i = 0; i < months; i++)
{
decimal thisMonth = Math.Round(balance / monthsRemaining, 2);
balance -= thisMonth;
monthsRemaining -= 1;
Console.WriteLine("Month {0}: {1}", i + 1, thisMonth);
}
This will result in 4.33, 4.34, 4.33.
The benefit of this method is that the rounding errors are distributed fairly evenly throughout the period rather than all in one month. For example, 100 over 24 months using that method would result in 23 payments of 4.17 and 1 of 4.09 whereas reducing balance would be 4.16 or 4.17 each month.

You do not have to check the remainder. A more efficient C# code (in terms of the required computation) would be like the following.
double amount = 13;
int months = 3;
int precision = 2;
double[] amountForEachMonth = new double[months];
double temp = Math.Round(amount / months, precision);
for (int i = 0 ; i < months - 1 ; i++)
amountForEachMonth[i] = temp;
amountForEachMonth[months - 1] = amount - (temp * (months - 1)) ;

You don't need to make it a special case when there is a discrepancy, you can simply always calculate the payment of the last month as what's left to pay to reach the total amount. If there is no discrepancy then it will be the same value anyway. Example:
int months = Convert.ToInt32(txtMonths.Value);
decimal amount = Convert.ToDecimal(txtAmount.Value);
month_per_amount = Decimal.Round(amount / months, 2);
decimal last_month = amount - (months - 1) * month_per_amount;
for (int month = 1; month <= months; month++) {
decimal monthly = month < months ? month_per_amount : last_month;
Console.WriteLine("Month {0}: {1}", month, monthly);
}

if " amount % month == 0 " , no discrepancy occures. otherwise, the last item should be a little more than others .
(The code here may have some syntax issues, I wanted to show you the algorithm.)
decimal amount = Convert.ToDecimal(txtAmount.Value);
int month = Convert.ToInt32(txtMonths.Value);
int n = 3;
decimal amounts[3];//n = 3
for (int i = 0 ; i < n-1 ; i++)
amounts[i] = amount / month;
if ( amount % month != 0 ) {
amounts[n-1] = amount - ( amount / month * (n-1) ) ;
else
amounts[n-1] = amount / month ;

Related

Divide Into Monthly Installments

Hi I'm trying to make Monthly Installments of a "double value"
The Problem is that the decimal values get divided too, and i don't need that happen.
Example :
List<Installments> InstallmentList {get; set;}
for (int i = 0 ; int i <= Month ; i++)
{
double Value = 90.10 ;
int Month = 3;
InstallmentCost = Value / Month;
InstallmentList.Add (new Installment {InstallmentCost = example.InstallmentCost} )
}
Doing That i will get a list of Installments where the value will be :
Installment = 30.03333333333333;
Installment = 30.03333333333333;
Installment = 30.03333333333333;
But I need that the decimals do not divide and and only the last Installment gets it
Example Of The Results that i need :
Installment = 30.00;
Installment = 30.00;
Installment = 30.10;
Just truncate the installment which only takes the integral part (if not C# then convert to something like int and back to double would do the trick!).
I have used C#, here's the working solution:-
double Value = 90.10;
int Month = 3;
for (int i = 1; i <= Month ; i++)
{
var installmentCost = Math.Truncate(Value / Month);
InstallmentList.Add(new Installment {InstallmentCost = installmentCost});
}
// Extract pending balance to be adjusted, total - the sum of all installments
double pendingBalanceToAdjust = Value - InstallmentList.Sum((s) => s.InstallmentCost);
// Update to the last installment
if (pendingBalanceToAdjust > 0)
InstallmentList.Last().InstallmentCost += pendingBalanceToAdjust;
You can calculate the remainder at the start and then divide the rest into equal parts:
double value = 90.10;
int month = 3;
// calculate the remainder with precision 0.1
double remainder = value % (month * 0.1);
double installmentValue = (value - remainder) / month;
for (int i = 0; i < month - 1; i++)
InstallmentList.Add(new Installment {InstallmentCost = installmentCost});
InstallmentList.Add(new Installment {InstallmentCost = installmentCost + remainder});
the expression value % (month * 0.1) effectively works out what is left over if you keep giving each of the 3 months 0.1 from the value until you can no longer carry on.
Changing the precision to 0.01 will change the outcome to: 30.03, 30.03, 30.04
Linq approach
decimal value = 90.10m;
int month = 3;
List<Installment> installments = Enumerable.Range(0, month).Select(x => new Installment() { InstallmentCost = Math.Floor(value / month) }).ToList();
installments.Last().InstallmentCost += (value - installments.Sum(x => x.InstallmentCost));

How to divide a decimal number into rounded parts that add up to the original number?

All Decimal numbers are rounded to 2 digits when saved into application. I'm given a number totalAmount and asked to divide it into n equal parts(or close to equal).
Example :
Given : totalAmount = 421.9720; count = 2 (totalAmount saved into application is 421.97)
Expected : 210.99, 210.98 => sum = 421.97
Actual(with plain divide) : 210.9860 (210.99), 210.9860 (210.99) => sum = 412.98
My approach :
var totalAmount = 421.972m;
var count = 2;
var individualCharge = Math.Floor(totalAmount / count);
var leftOverAmount = totalAmount - (individualCharge * count);
for(var i = 0;i < count; i++) {
Console.WriteLine(individualCharge + leftOverAmount);
leftOverAmount = 0;
}
This gives (-211.97, -210)
public IEnumerable<decimal> GetDividedAmounts(decimal amount, int count)
{
var pennies = (int)(amount * 100) % count;
var baseAmount = Math.Floor((amount / count) * 100) / 100;
foreach (var _ in Enumerable.Range(1, count))
{
var offset = pennies-- > 0 ? 0.01m : 0m;
yield return baseAmount + offset;
}
}
Feel free to alter this if you want to get an array or an IEnumerable which is not deferred. I updated it to get the baseAmount to be the floor value so it isn't recalculated within the loop.
Basically you need to find the base amount and a total of all the leftover pennies. Then, simply add the pennies back one by one until you run out. Because the pennies are based on the modulus operator, they'll always be in the range of [0, count - 1], so you'll never have a final leftover penny.
You're introducing a few rounding errors here, then compounding them. This is a common problem with financial data, especially when you have to constrain your algorithm to only produce outputs with 2 decimal places. It's worse when dealing with actual money in countries where 1 cent/penny/whatever coins are no longer legal tender. At least when working with electronic money the rounding isn't as big an issue.
The naive approach of dividing the total by the count and rounding the results is, as you've already discovered, not going to work. What you need is some way to spread out the errors while varying the output amounts by no more than $0.01. No output value can be more than $0.01 from any other output value, and the total must be the truncated total value.
What you need is a way to distribute the error across the output values, with the smallest possible variation between the values in the result. The trick is to track your error and adjust the output down once the error is high enough. (This is basically how the Bresenham line-drawing algorithm figures out when to increase the y value, if that helps.)
Here's the generalized form, which is pretty quick:
public IEnumerable<decimal> RoundedDivide(decimal amount, int count)
{
int totalCents = (int)Math.Floor(100 * amount);
// work out the true division, integer portion and error values
float div = totalCents / (float)count;
int portion = (int)Math.Floor(div);
float stepError = div - portion;
float error = 0;
for (int i = 0; i < count; i++)
{
int value = portion;
// add in the step error and see if we need to add 1 to the output
error += stepError;
if (error > 0.5)
{
value++;
error -= 1;
}
// convert back to dollars and cents for outputput
yield return value / 100M;
}
}
I've tested it with count values from 1 through 100, all outputs sum to match the (floored) input value exactly.
Try to break it down to steps:
int decimals = 2;
int factor = (int)Math.Pow(10, decimals);
int count = 2;
decimal totalAmount = 421.97232m;
totalAmount = Math.Floor(totalAmount * factor) / factor; // 421.97, you may want round here, depends on your requirement.
int baseAmount = (int)(totalAmount * factor / count); // 42197 / 2 = 21098
int left = (int)(totalAmount * factor) % count; // 1
// Adding back the left for Mod operation
for (int i = 0; i < left; i++)
{
Console.WriteLine((decimal)(baseAmount + 1) / factor); // 21098 + 1 / 100 = 210.99
}
// The reset that does not needs adjust
for (int i = 0; i < count - left; i++)
{
Console.WriteLine((decimal)baseAmount / factor); // 21098 / 100 = 210.98
}

Why my function that calculates pi number is freezing?

I need to calculate pi number until 15th digit but my function freezes. I use this Taylor's series:
atan(x) = \sum_{n=0}^\infty \frac{(-1)^{n} \cdot x^{2n + 1}}{2n + 1}
And x equals 1.
There is my function:
public static decimal Pi()
{
decimal curr = 4m,
prev = 0m,
one = -1m,
den = 3.0m;
while (Math.Abs(curr - prev) > 1e-15m)
{
prev = curr;
curr += 4.0m * one / den;
one = -one;
den += 2.0m;
}
return curr;
}
I have debugged it, but i did't find why. Link to REPL
The problem is that the algorithm is exponential on the number of digits of precision that you want. To demonstrate I've changed your code a bit to track the number of iterations before we get a result with more and more precision
decimal curr = 4m,
prev = 0m,
one = -1m,
den = 3.0m;
int i = 0;
decimal epsilon = 1;
while(true)
{
prev = curr;
curr += 4.0m * one / den;
one = -one;
den += 2.0m;
i++;
if(Math.Abs(curr - prev) <= epsilon)
{
Console.WriteLine(curr);
Console.WritleLine(i);
epsilon /= 10m;
}
}
Here's the resulting output after it gets to 8 digits of precision.
3.4666666666666666666666666667
2
3.1891847822775947292439110472
20
3.1465677471829564012877876609
200
3.1420924036835276076022995132
2000
3.1416426510898869869000847891
20000
3.1415976535647933322124871234
200000
3.1415931535895432385563933310
2000000
3.1415927035897907384627370503
20000000
3.1415926585897932134626435385
200000000
as you can see each additional digit of percision take 10 times as many iterations and thus 15 digits will take 10,000,000 times as long as it takes to get 8.
From the formula,
|cur - prev| = 1 / (2n+1) + 1 / (2n-1)
Your function should be working correctly, you just have to wait until the 250,000,000,000,000th term. A little of patience (only a few days).
But there is no guarantee that you get 15 exact digits in the end.
Never use Leibnitz' formula. Use Machin's variant. https://en.wikipedia.org/wiki/Machin-like_formula

How to round up to the higher 10 in C#?

I have to write a program at school that allows the user to input a 7-digits of a GTIN-8 barcode and calculate check digit. To calculate the check digit the seven input numbers are multiplied by 3, 1, 3, 1 etc.
for (i = 0; i < 7; i++)
{
//Convert String to Character
ch = gtinNum[i];
//Convert Character to Integer
number = Convert.ToInt32(ch);
product = number * weights[i];
total = total + product;
}//end for loop
After that, the total is taken away from the nearest higher ten. For example if the total was 43 then it would be 50-43.
In the simplest way possible, how do I round up to the higher ten?
You can use Math.Ceiling, you just need to divide by ten first and multiply by 10 afterwards. Divide by 10.0 to avoid integer division(Math.Ceiling takes double):
int num = 43;
int nextHigherTen = (int)Math.Ceiling(num / 10.0) * 10;
This "rounds" 41-50 up to 50. If you want it from 40-49 you could use this:
int nextHigherTen = (int)Math.Ceiling((num + 1) / 10.0) * 10;

spliting 6 digit int into 3 parts?

I want to get a six digit number by user and spit it into 3 parts as(day, month, year)
Example:
int date=111213;
day =11;
month =12;
year =13;
I think I have to convert it into string then by using substring() I can do this.
Any easy Idea ??
How about:
// Assuming a more sensible format, where the logically most significant part
// is the most significant part of the number too. That would allow sorting by
// integer value to be equivalent to sorting chronologically.
int day = date % 100;
int month = (date / 100) % 100;
int year = date / 10000;
// Assuming the format from the question (not sensible IMO)
int year = date % 100;
int month = (date / 100) % 100;
int day = date / 10000;
(Do you have to store your data like this to start with? Ick.)
Storing a date as an integer like this isn't ideal, but if you must do it -- and you're sure that the number will always use the specified format -- then you can easily extract the day, month and year:
int day = date / 10000;
int month = (date / 100) % 100;
int year = date % 100;
You can do this with modular arithmetic:
int day = date / 10000;
int month = (date / 100) % 100;
int year = date % 100;
Here is the solution in Java with no optimization:
final int value = 111213;
int day;
int month;
int year;
day = value / 10000;
month = (value - (day * 10000)) / 100;
year = (value - (day * 10000)) - month * 100;

Categories

Resources