纠结于从价值中移除/添加多个税
本文关键字:添加 | 更新日期: 2023-09-27 17:53:07
我必须从一个值中删除/添加多个税或调整以返回到它所应用的原始值。我将定义调整的内容:
调整可以是复合或非复合的百分比。它也可以是一个固定的金额。它也可以添加或删除到初始值。我只需要把税后值和税后值反向的部分写出来。我写了一些可以用来生成测试数据的东西,我还写了一些可以从测试数据生成的税后值逆转这些调整的东西。我认为我在应用调整和撤消它们方面做得太过分了。调整是按顺序应用的,所以一个包含(+7%非复利,+3%复利,+ 5%非复利)的列表将首先应用7,然后是3,然后是5,在这种情况下,我认为要删除它,我必须往回走,意思是删除5,然后删除3,然后删除7。下面是我将调整应用于初始值的程序(它应该返回115.21,但在本例中它返回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;
}
}
}
这是取消应用前一算法的调整的算法。注意,当我将它们添加到列表中时,我颠倒了顺序,所以当我取115.21时,我返回到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;
}
}
}
我遇到的主要问题是,如果所有的调整都是复合的,或者如果它们都是非复合的,或者如果它们都是平坦的,我可以让它工作,但是当我开始混合复合和非复合百分比时,我可以添加或删除它们,例如(+5%非复合,-2$,-3%复合,+4%非复合)
非复利税是根据初始金额去除或添加的,因此,如果初始金额为100,并且您有+3%和-4%的非复利税,那么您首先将100的3%加起来得到103,然后从103中减去100的4%得到99。
如果4%是复利的,你将从103中扣除4%,而不是100,所以它将是:
103/1.04 = 99.03846.....
基于Lasse答案的场景:
添加所有非复合百分比通过。
减去所有非复合百分比通过。
添加所有复合百分比通过。
添加所有的平量通过。减去所有的平量通过。
添加非复合/复合百分比通过。
减去所有复合百分比失败:
使用-7%,-3%,-5%。带计算器:
100/1.07/1.03/1.05 = 86.41511227483462,但我得到85.6995000
减去非复合/复合失败:
使用-7%化合物,-3%非化合物,-5%化合物,带计算器:
((100/1.07) - 3)/1.05 = 86.1504…,但我得到了85万。
基本上,当我将复利/非复利与加减数量混合时,它开始不能产生正确的结果。
不通过的调整:
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,我又看了一遍场景,我已经对此做了评论,但我相信我用计算器计算错了。用另一种方法计算后,我的数字与你的相符,所有的情况都通过了。例如,给定:
var adjustments = new Adjustment[]
{
new NonCompoundTaxAdjustment(7M),
new NonCompoundTaxAdjustment(3M),
new CompoundTaxAdjustment(-5M)
};
我用计算器(100 * 1.1)/1.05 = 104.761904来做这个,但后来我尝试了
100 * 1.1 = 110110 * 0.05 = 5.5110 - 5.5 = 104.5,这与你的计算相匹配,所以我猜你是这样处理的。
一个想法:
如果你用100减去7%,这样做:
100 -(100 * 0.07) = 93。现在看起来是不正确的,因为把7%加回去,也就是93 * 1.07,你得到的不是100,而是99.51。从100中减去7%实际上应该是100/1.07 = 93.45,当你用93.45 * 1.07时,你回到100
我被卡在这里了。
当前代码似乎只正确处理添加百分比。例如,如果我把+7%加到200上,我得到214,这是正确的,而要回到200,代码做214/1.07,这也是正确的。问题是,如果我想从214中删除7%,代码正在做。93 * 200 = 186,这是不正确的。200减去7%实际上是200/1.07 = 186.9158878504673。将这个值乘以7%,即186.9158878504673 * 1.07 = 200,但如果我用186 * 1.07,我得到199.02,而不是200。
好的,我是这样做的。
我从一个由两个数字组成的公式开始,一个因子和一个偏移量。
公式如下:
result = input * factor + offset
公式开始时因子为1,偏移量为0,所以基本上未经调整的公式是这样的:
result = input * 1 + 0
result = input * 1
result = input <-- as expected
然后,对于每次调整,我将公式调整如下:
- 平值:将平值添加到偏移
- 复合税:将因子和抵消量同时乘以
1 + PERCENTAGE/100
- 非复利税:加上
PERCENTAGE/100
的价值因子。(edit: was 1+,这是错误的)
这意味着你的例子:
- 7%非复利税
- 3%复合税
- 5%非复利税
的结果如下:
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
要计算100是多少,在加上税之后,你把它输入公式,得到:
result = 100 * 1.1521 + 0
result = 115.21
要计算115.21是多少,在加上税之前,你可以通过求解输入来颠倒公式:
result = input * factor + offset
result - offset = input * factor
(result - offset) / factor = input
input = (result - offset) / factor
:
input = (result - 0) / 1.1521
input = result / 1.1521
你可以拿回你的100。
可以在LINQPad中测试的代码如下: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);
}
}
我给出的例子,看起来像这样,让我们先手工做。
- 1%复合、1%非复合、1%复合、1%复合
- 从100开始,加上1%的复利税,得到101
- 加上1%的非复利税,100的1%,得到102
- 添加1的平坦值,得到103
- 增加1%的复合税,得到104,03
- 增加1%的非复利税,100的1%,得到105,03
- 增加1%的复利税,得到106,0803
代码的输入:
var adjustments = new Adjustment[]
{
new CompoundTaxAdjustment(1M),
new NonCompoundTaxAdjustment(1M),
new FlatValueAdjustment(1M),
new CompoundTaxAdjustment(1M),
new NonCompoundTaxAdjustment(1M),
new CompoundTaxAdjustment(1M)
};
输出:106,0803000
100
我就是这么做的。它适用于大多数情况,特别是如果你把非复利百分比放在首位。如果有人有任何改进或注意到任何错误,请告诉我:
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;
}
}
}