我有一个问题,我认为可能没有其他答案,然后完全重新考虑应用程序,但希望你们可以证明我错了!
我有一个应用程序使用汇率将货币值的基值计算回四位小数。计算非常简单,每次都通过所有相关的单元测试得出正确的结果:
public static decimal GetBaseValue(decimal currencyAmount, RateOperator rateOperator,
double exchangeRate)
{
if (exchangeRate <= 0)
{
throw new ArgumentException(ErrorType.
ExchangeRateLessThanOrEqualToZero.ErrorInfo().Description);
}
decimal baseValue = 0;
if (currencyAmount != 0)
{
switch (rateOperator)
{
case RateOperator.Divide:
baseValue = Math.Round(currencyAmount * Convert.ToDecimal(exchangeRate),
4, MidpointRounding.AwayFromZero);
break;
case RateOperator.Multiply:
baseValue = Math.Round(currencyAmount / Convert.ToDecimal(exchangeRate),
4, MidpointRounding.AwayFromZero);
break;
default:
throw new ArgumentOutOfRangeException(nameof(rateOperator));
}
}
return baseValue;
}
我也有相当于从base计算货币,未显示以避免混淆问题,并且在任何情况下除了参数名称和switch语句中的数学运算符的反转之外几乎完全相同的代码。
当我需要将此流程应用于具有多行的事务时,我的问题出现了。规则是借方总额必须等于贷方总额,但有些数字只是一分钱的一小部分。这是因为我们总计两次或多次单独转换的数字与单次转换的结果,我相信累积的舍入会导致错误。
让我们假设以下内容:
现在让我们回顾一下示例交易:
Line # Debit Credit
1 $1.20
2 $1.00
3 $0.20
这通过了测试,因为总学分与总借记相匹配。
当我们转换(将每个美元价值除以1.454)时,我们会看到问题:
Line # Debit Credit
1 £0.8253
2 £0.6878
3 £0.1376
========================
Total £0.8254 £0.8253
========================
这会失败并违反规则,我相信借记栏中的两组舍入和信用列中只有一组。然而,我需要能够准确地计算倒退和转发,并且我需要确保交易在所有行中以货币和基准进行余额。
所以我的问题是:如何最好地解决这个问题?任何人都经历过类似的事情,如果是这样,你是否介意分享你如何解决它?
编辑 - 我使用的解决方案
根据查尔斯的回答,我完全指出了正确的方向,我发布了我的工作方法,以造福其他可能面临类似问题的人。虽然理解这个特定的代码可能不适合直接复制和粘贴解决方案,但我希望这些评论和程序会有所帮助:
private static void SetBaseValues(Transaction transaction)
{
// Get the initial currency totals
decimal currencyDebitRunningTotal = transaction.CurrencyDebitTotal;
decimal currencyCreditRunningTotal = transaction.CurrencyCreditTotal;
// Only one conversion, but we do one per column
// Note that the values should be the same anyway
// or the transaction would be invalid
decimal baseDebitRunningTotal =
Functions.GetBaseValue(currencyDebitRunningTotal,
transaction.MasterLine.RateOperator,
transaction.MasterLine.ExchangeRate);
decimal baseCreditRunningTotal =
Functions.GetBaseValue(currencyCreditRunningTotal,
transaction.MasterLine.RateOperator,
transaction.MasterLine.ExchangeRate);
// Create a list of transaction lines that belong to this transaction
List<TransactionLineBase> list = new List<TransactionLineBase>
{ transaction.MasterLine };
list.AddRange(transaction.TransactionLines);
// If there is no tax line, don't add a null entry
// as that would cause conversion failure
if (transaction.TaxLine != null)
{
list.Add(transaction.TaxLine);
}
// Sort the list ascending by value
var workingList = list.OrderBy(
x => x.CurrencyCreditAmount ?? 0 + x.CurrencyDebitAmount ?? 0).ToList();
// Iterate the lines excluding any entries where Credit and Debit
// values are both null (this is possible on some rows on
// some transactions types e.g. Reconciliations
foreach (var line in workingList.Where(
line => line.CurrencyCreditAmount != null ||
line.CurrencyDebitAmount != null))
{
if (transaction.CanConvertCurrency)
{
SetBaseValues(line);
}
else
{
var isDebitLine = line.CurrencyCreditAmount == null;
if (isDebitLine)
{
if (line.CurrencyDebitAmount != 0)
{
line.BaseDebitAmount =
line.CurrencyDebitAmount ?? 0 /
currencyDebitRunningTotal * baseDebitRunningTotal;
currencyDebitRunningTotal -=
line.CurrencyDebitAmount ?? 0;
baseDebitRunningTotal -= line.BaseDebitAmount ?? 0;
}
}
else
{
if (line.CurrencyCreditAmount != 0)
{
line.BaseCreditAmount =
line.CurrencyCreditAmount ?? 0/
currencyCreditRunningTotal*baseCreditRunningTotal;
currencyCreditRunningTotal -= line.CurrencyCreditAmount ?? 0;
baseCreditRunningTotal -= line.BaseCreditAmount ?? 0;
}
}
}
}
}
答案 0 :(得分:1)
这取决于具体情况,但其中一个选项是仅转换总计,然后使用减少余额法按比例分配给每个部分。这可以确保零件的总和将始终完全等于总和。
您的借记卡和信用额度加起来都是1.20美元,因此您可以按照自己的汇率兑换为0.8253英镑。
然后按比例将此分配给您从最小到最大的借方金额。排序背后的理论是,你不太可能关心更多数量的额外/丢失的便士。
所以你从1.20美元和0.6878英镑的总数开始,然后计算适用于你最小美元金额的转换余额的比例:
$0.20 / $1.20 * 0.8253 = £0.1376
然后从总计中扣除金额(这是'减少余额'部分):
$1.20 - $0.20 = $1.00
£0.8253 - £0.1376 = £0.6877
然后计算下一个最大值(因为在这个例子中你只有1个量,这是微不足道的):
$1.00 / $1.00 * £0.6877 = £0.6877
所以这会给你:
Line # Debit Credit
1 £0.8253
2 £0.6877
3 £0.1376
========================
Total £0.8253 £0.8253
========================
答案 1 :(得分:0)
这通常是一个业务问题,而不是编程问题。您所看到的结果是数学四舍五入的结果。在财务计算中,企业将决定何时应用汇率并且必须应用一次。例如,在您的情况下,可能会决定将其应用于项目值,然后乘以增值税以获得总计。这也通常出现在您所在地区的会计/财务接受标准中。