Stuck at removing/adding multiple taxes from a value?
I have to remove/add mu开发者_StackOverflowltiple taxes or adjustments from a value to get back to the original value that it was applied to. I will define what an adjustment can be:
An adjustment can be a percentage that is either compounded or non-compounded. It can also be a flat dollar amount. It can also be added or removed to the initial value. I just have to write the part that reverses the taxes from the post-tax value. I wrote something up I could use to generate test data and I wrote something else that would reverse those adjustments from the post-tax value generated by the test data. I think I am overengineering for applying the adjustments and unapplying them. The adjustments are applied order, so a list containing (+7% non-compounded, +3% compounded, + 5% non-compounded) would have the 7 applied first, then the 3 and then the 5 and in this case, I believe to remove it I have to go backwards, meaning remove the 5, then the 3 and then the 7. Here is my program to apply the adjustments to an initial value (it should bring back 115.21, but in this case it is bringing back 115.0)
void Main()
{
Adjustment a1 = new Adjustment {Amount = 7.0M, IsCompounded = false, Add = true, Percent = true};
Adjustment a2 = new Adjustment {Amount = 3.0M, IsCompounded = true, Add = true, Percent = true};
Adjustment a3 = new Adjustment {Amount = 5.0M, IsCompounded = false, Add = true ,Percent = true};
List<Adjustment> adjustments = new List<Adjustment>();
adjustments.Add(a1);
adjustments.Add(a2);
adjustments.Add(a3);
decimal total = 100m;
decimal adjustedTotal = total;
decimal nonCompoundValues = 0.0m;
string prevTypeCalc = "";
decimal compoundValues = 1.0m;
decimal percents = 1.0m;
int i = 0;
foreach(Adjustment a in adjustments)
{
if(a.Percent)
{
if(a.IsCompounded)
{
if(a.Add)
{
compoundValues *= a.CompoundedValue;
}
else
{
compoundValues /= a.CompoundedValue;
}
prevTypeCalc = "Compound";
}
else if(!a.IsCompounded)
{
if(a.Add)
{
nonCompoundValues += a.AmountFraction;
}
else
{
nonCompoundValues -= a.AmountFraction;
}
prevTypeCalc = "Non-Compound";
}
}
else
{
if(prevTypeCalc == "Non-Compound" || prevTypeCalc == "Compound")
{
if(nonCompoundValues <= 0)
adjustedTotal *= compoundValues - Math.Abs(nonCompoundValues);
else
adjustedTotal *= compoundValues + Math.Abs(nonCompoundValues);
compoundValues = 1.0m;
nonCompoundValues = 0.0m;
}
if(a.Add)
{
adjustedTotal += a.Amount;
}
else
{
adjustedTotal -= a.Amount;
}
prevTypeCalc = "Flat";
}
}
if(prevTypeCalc == "Non-Compound" || prevTypeCalc == "Compound")
{
if(nonCompoundValues <= 0)
adjustedTotal *= compoundValues - Math.Abs(nonCompoundValues);
else
adjustedTotal *= compoundValues + Math.Abs(nonCompoundValues);
}
Console.WriteLine(adjustedTotal);
}
public class Adjustment
{
public bool Percent {get;set;}
public decimal Amount {get;set;}
public bool IsCompounded {get;set;}
public bool Add{get;set;}
public decimal AmountFraction
{
get {
return Amount/100.0M;
}
}
public decimal CompoundedValue
{
get{
return 1 + AmountFraction;
}
}
}
Here is the algorithm for unapplying the adjustments from teh previous algorithm. Notice, I have reversed the order in when I add them to the list, so when I take 115.21, I get back to 100:
void Main()
{
Adjustment a1 = new Adjustment {Amount = 7.0M, IsCompounded = false, Add = false, Percent = true};
Adjustment a2 = new Adjustment {Amount = 3.0M, IsCompounded = true, Add =false, Percent = true};
Adjustment a3 = new Adjustment {Amount = 5.0M, IsCompounded = false, Add = false, Percent = true};
List<Adjustment> adjustments = new List<Adjustment>();
adjustments.Add(a3);
adjustments.Add(a2);
adjustments.Add(a1);
decimal total = 115.21m;
int i = 0;
decimal adjustedTotal = total;
decimal nonCompoundValues = 0.0m;
string prevTypeCalc = "";
decimal compoundValues = 1.0m;
bool nonCompoundFirst = true;
bool first = true;
foreach(Adjustment a in adjustments)
{
if(a.Percent)
{
if(a.IsCompounded)
{
if(a.Add)
{
compoundValues *= a.CompoundedValue;
}
else
{
if(prevTypeCalc == "")
compoundValues = a.CompoundedValue;
else
compoundValues /= a.CompoundedValue;
}
prevTypeCalc = "Compound";
}
else if(!a.IsCompounded)
{
if(a.Add)
{
nonCompoundValues += a.AmountFraction;
}
else
{
nonCompoundValues -= a.AmountFraction;
}
prevTypeCalc = "Non-Compound";
}
}
else
{
if(prevTypeCalc == "Non-Compound" || prevTypeCalc == "Compound")
{
if(nonCompoundValues <= 0 && compoundValues != 1) //Non-Compound
adjustedTotal *= compoundValues + Math.Abs(nonCompoundValues);
else if(nonCompoundValues <= 0 && compoundValues == 1) //Compound
adjustedTotal /= compoundValues + Math.Abs(nonCompoundValues);
else
adjustedTotal /= compoundValues - Math.Abs(nonCompoundValues); //Compound + Non-Compound
compoundValues = 1.0m;
nonCompoundValues = 0.0m;
}
if(a.Add)
adjustedTotal += a.Amount;
else
adjustedTotal -= a.Amount;
prevTypeCalc = "Flat";
}
}
if(prevTypeCalc == "Non-Compound" || prevTypeCalc == "Compound")
{
if(nonCompoundValues <= 0 && compoundValues != 1)
adjustedTotal /= compoundValues + Math.Abs(nonCompoundValues);
else if(nonCompoundValues <= 0 && compoundValues == 1) //Non-compound
adjustedTotal /= compoundValues + Math.Abs(nonCompoundValues);
else
adjustedTotal /= compoundValues - Math.Abs(nonCompoundValues);
}
Console.WriteLine(adjustedTotal);
}
public class Adjustment
{
public bool Percent {get;set;}
public decimal Amount {get;set;}
public bool IsCompounded {get;set;}
public bool Add{get;set;}
public decimal AmountFraction
{
get {
return Amount/100.0M;
}
}
public decimal CompoundedValue
{
get{
return 1 + AmountFraction;
}
}
}
The main problem I have is that I can get it to work if all the adjustment are compound or if they are all non-compound or if they are all flat, but it gets crazy when I start mixing compound and non-compound percentages with the fact that I can add or remove them as well, for example (+5% non-compound, -2$, -3% compound, +4% non-compound)
Non-Compounded Tax is removed or added based up the initial amount, so if your initial amount is 100 and you have a non-compounding taxes of +3% and -4%, you first add 3% of 100 to get 103 and then you subtract 4% of 100 from 103 to get 99.
If 4% was compounded, you would take the 4% off the 103, not the 100, so it would be:
103 / 1.04 = 99.03846.....
Scenarios based upon Lasse's answer:
Adding all non-compound percentages passes.
Subtracting all non-compound percentages passes. Adding all compound percentages passes. Adding all flat amounts passes. Subtracting all flat amounts passes. Adding non-compound/compound percentages passes.Subtracting all compound percentages fails:
Using -7%, -3%, -5%. with a calculator:
100/ 1.07 / 1.03 / 1.05 = 86.41511227483462, but I get 85.6995000
Subtracting Non-Compound/Compound fails:
Using -7% compound, -3% non-compound, -5% compound, with a calculator:
((100 / 1.07) - 3) / 1.05 = 86.1504..., but I get 85.50000.
Basically, it starts not producing the correct results when I mix compounding/non-compounding with adding and subtracting the amounts.
Adjustments that don't pass:
var adjustments = new Adjustment[]
{
new CompoundTaxAdjustment(-7M),
new CompoundTaxAdjustment(-3M),
new CompoundTaxAdjustment(-5M)
};
var adjustments = new Adjustment[]
{
new CompoundTaxAdjustment(+7M),
new CompoundTaxAdjustment(-3M),
new CompoundTaxAdjustment(-5M)
};
var adjustments = new Adjustment[]
{
new CompoundTaxAdjustment(+7M),
new NonCompoundTaxAdjustment(-3M),
new CompoundTaxAdjustment(5M)
};
var adjustments = new Adjustment[]
{
new CompoundTaxAdjustment(+7M),
new FlatValueAdjustment(-3M),
new CompoundTaxAdjustment(5M)
};
Lasse, I wen through the scenarios again and I made a comment about this already, but I believe I am doing my calculation wrong with the calculator. After doing the calculation a different way, my numbers matched up with yours and all scenarios passed. For example, given:
var adjustments = new Adjustment[]
{
new NonCompoundTaxAdjustment(7M),
new NonCompoundTaxAdjustment(3M),
new CompoundTaxAdjustment(-5M)
};
I was doing this with the calculator (100 * 1.1) / 1.05 = 104.761904, but then I tried
100 * 1.1 = 110 110 * 0.05 = 5.5 110 - 5.5 = 104.5 which is what matches up with your calculations, so I guess you handle it this way.
A thought:
If you subtract 7% from 100 and do it this way:
100 - (100 * 0.07) = 93. it seems like it is incorrect now because to add 7% back, which is 93 * 1.07, you don't get 100, you get 99.51. Subtracting 7% from 100 should actually be 100/1.07 = 93.45 and when you take 93.45 * 1.07, you get back to 100
I am stuck at something here.
The current code only appears to handle adding percentages correctly. For example, if i add a +7% to 200, I get 214 which is correct and to get back to 200, the code does 214/1.07 which is also correct. The problem is that if I want to remove 7% from 214, the code is doing .93 * 200 = 186, which is incorrect. 7% removed from 200 is actually 200/1.07 = 186.9158878504673. Taking this value and multiplying it by 7% or 186.9158878504673 * 1.07 = 200, but if I take 186 * 1.07, I get 199.02 which is not 200.
Ok, here's what I did.
I started with a formula consisting of two numbers, a factor and an offset.
The formula looks like this:
result = input * factor + offset
The formula starts out with a factor of 1, and an offset of 0, so basically the unadjusted formula looks like this:
result = input * 1 + 0
result = input * 1
result = input <-- as expected
Then, for each adjustment, I adjust the formula as follows:
- Flat value: Add the flat value to the offset
- Compounded tax: multiply both the factor and the offset by
1 + PERCENTAGE/100
- Noncompounded tax: add to the factor the value of
PERCENTAGE/100
. (edit: was 1+, that was incorrect)
This means that your example of:
- 7% Non-compounded tax
- 3% Compounded tax
- 5% Non-compounded tax
results in this:
result = input * factor + offset
result = input * 1 + 0
result = input * 1.07 + 0 <-- add 0.07 to factor
result = input * 1.1021 + 0 <-- multiply both factor and offset by 1.03
result = input * 1.1521 + 0 <-- add 0.05 to factor
To calculate how 100 would be, after adding the taxes, you feed it through the formula and get:
result = 100 * 1.1521 + 0
result = 115.21
To calculate how 115.21 was, before adding the taxes, you reverse the formula by solving it for input:
result = input * factor + offset
result - offset = input * factor
(result - offset) / factor = input
input = (result - offset) / factor
So:
input = (result - 0) / 1.1521
input = result / 1.1521
and you get back your 100.
The code, which you can test in LINQPad is as follows:
void Main()
{
var adjustments = new Adjustment[]
{
new CompoundTaxAdjustment(7M),
new NonCompoundTaxAdjustment(3M),
new CompoundTaxAdjustment(5M)
};
var original = 100M;
var formula = Adjustment.GenerateFormula(adjustments);
var result = formula.Forward(original).Dump(); // prints 115,5
var newOriginal = formula.Backward(result).Dump(); // prints 100
}
public abstract class Adjustment
{
public class Formula
{
public decimal Factor = 1.0M;
public decimal Offset;
public decimal Forward(decimal input)
{
return input * Factor + Offset;
}
public decimal Backward(decimal input)
{
return (input - Offset) / Factor;
}
}
public static Formula GenerateFormula(IEnumerable<Adjustment> adjustments)
{
Formula formula = new Formula();
foreach (var adjustment in adjustments)
adjustment.Adjust(formula);
return formula;
}
protected abstract void Adjust(Formula formula);
}
public class FlatValueAdjustment : Adjustment
{
private decimal _Value;
public FlatValueAdjustment(decimal value)
{
_Value = value;
}
protected override void Adjust(Formula formula)
{
formula.Offset += _Value;
}
}
public abstract class TaxAdjustment : Adjustment
{
protected TaxAdjustment(decimal percentage)
{
Percentage = percentage;
}
protected decimal Percentage
{
get;
private set;
}
}
public class CompoundTaxAdjustment : TaxAdjustment
{
public CompoundTaxAdjustment(decimal percentage)
: base(percentage)
{
}
protected override void Adjust(Formula formula)
{
var myFactor = 1M + Percentage / 100M;
formula.Offset *= myFactor;
formula.Factor *= myFactor;
}
}
public class NonCompoundTaxAdjustment : TaxAdjustment
{
public NonCompoundTaxAdjustment(decimal percentage)
: base(percentage)
{
}
protected override void Adjust(Formula formula)
{
formula.Factor += (Percentage / 100M);
}
}
The example I gave, would look like this, let's do it manually first.
- 1% compounded, 1% non-compounded, flat value of 1, 1% compounded, 1% non-compounded, 1% compounded
- Start at 100, add 1% compounded tax, getting 101
- Add 1% non-compounded tax, 1% of 100, getting 102
- Add a flat value of 1, getting 103
- Add a 1% compoundex tax, getting 104,03
- Add a 1% non-compounded tax, 1% of 100, getting 105,03
- Add a 1% compounded tax, getting 106,0803
The input to the code:
var adjustments = new Adjustment[]
{
new CompoundTaxAdjustment(1M),
new NonCompoundTaxAdjustment(1M),
new FlatValueAdjustment(1M),
new CompoundTaxAdjustment(1M),
new NonCompoundTaxAdjustment(1M),
new CompoundTaxAdjustment(1M)
};
Output:
106,0803000
100
This is the way I did it. It works for most situations, especially if you put the non-compounding percentages first. If anyone has any improvements or notices any bugs, please let me know:
void Main()
{
Adjustment a1 = new Adjustment {Amount = 12.0M, IsCompounded = false, Add = false, Percent = false};
Adjustment a2 = new Adjustment {Amount = 3.0M, IsCompounded = true, Add = true, Percent = true};
Adjustment a3 = new Adjustment {Amount = 5.0M, IsCompounded = true, Add = true ,Percent = true};
List<Adjustment> adjustments = new List<Adjustment>();
adjustments.Add(a3);
adjustments.Add(a2);
adjustments.Add(a1);
decimal total = 103.55987055016181229773462783m;
decimal adjustedTotal = total;
decimal nonCompoundValues = 0.0m;
decimal compoundValues = 1.0m;
string prevType = "";
for(int i = 0; i <= adjustments.Count - 1; i++)
{
if(adjustments[i].Percent)
{
if(adjustments[i].IsCompounded)
{
if(i == adjustments.Count - 1 && adjustments[i].IsCompounded)
{
if(adjustments[i].Add)
{
nonCompoundValues += adjustments[i].Amount/100.0m;
}
else
{
nonCompoundValues -= adjustments[i].Amount/100.0m;
}
break;
}
if(nonCompoundValues < 0 & prevType != "Compound") //Remove tax
{
adjustedTotal /= compoundValues + Math.Abs(nonCompoundValues);
nonCompoundValues = 0.0m;
compoundValues = 1.0m;
}
else if(nonCompoundValues > 0 & prevType != "Compound") //Add tax
{
adjustedTotal *= compoundValues + Math.Abs(nonCompoundValues);
nonCompoundValues = 0.0m;
compoundValues = 1.0m;
}
if(adjustments[i].Add)
{
if(prevType == "" || prevType == "Compound")
{
adjustedTotal *= 1 + adjustments[i].Amount/100.0m; //add compound first
compoundValues = 1.0m;
}
else
{
compoundValues *= 1 + adjustments[i].Amount/100.0m;
}
}
else
{
if(prevType == "" || prevType == "Compound")
{
adjustedTotal /= 1 + adjustments[i].Amount/100.0m;
compoundValues = 1.0m;
}
else
{
compoundValues /= 1 + adjustments[i].Amount/100.0m;
}
}
prevType = "Compound";
}
else // Non-Compound
{
if(adjustments[i].Add)
{
nonCompoundValues += adjustments[i].Amount/100.0m;
}
else
{
nonCompoundValues -= adjustments[i].Amount/100.0m;
}
prevType = "Non-compound";
}
}
else //flat
{
if(nonCompoundValues < 0) //Remove tax
{
adjustedTotal /= compoundValues + Math.Abs(nonCompoundValues);
nonCompoundValues = 0.0m;
compoundValues = 1.0m;
}
else if(nonCompoundValues > 0) //Add tax
{
adjustedTotal *= compoundValues + Math.Abs(nonCompoundValues);
nonCompoundValues = 0.0m;
compoundValues = 1.0m;
}
if(adjustments[i].Add)
{
adjustedTotal += adjustments[i].Amount;
}
else
{
adjustedTotal -= adjustments[i].Amount;
}
}
}
if(nonCompoundValues < 0)
{
adjustedTotal /= compoundValues + Math.Abs(nonCompoundValues);
}
else
{
adjustedTotal *=compoundValues + Math.Abs(nonCompoundValues);
}
Console.WriteLine(adjustedTotal);
}
public class Adjustment
{
public bool Percent {get;set;}
public decimal Amount {get;set;}
public bool IsCompounded {get;set;}
public bool Add{get;set;}
public decimal AmountFraction
{
get {
return Amount/100.0M;
}
}
public decimal CompoundedValue
{
get{
return 1 + AmountFraction;
}
}
}
精彩评论