我支持金融 .net 应用程序。有很多关于在金融方面使用十进制数据类型的建议。
现在我被这个问题困住了:
decimal price = 1.0m/12.0m;
decimal quantity = 2637.18m;
decimal result = price * quantity; //results in 219.76499999999999999999999991
Run Code Online (Sandbox Code Playgroud)
问题是向客户收取的正确值是 219.77(舍入函数,MidpointRounding.AwayFromZero),而不是 219.76。
如果我将所有内容更改为双倍,它似乎有效:
double price = 1.0/12.0;
double quantity = 2637.18;
double result = price * quantity; //results in 219.765
Run Code Online (Sandbox Code Playgroud)
我要把所有东西都改成双倍吗?分数还会有其他问题吗?
我认为这个问题与Difference Betweendecimal, float and double in .NET?不同。因为它并没有真正向我解释为什么使用更精确的数据类型十进制的结果比使用更少字节的双精度数据类型的结果更不准确(在上面的示例中)。
推荐使用小数的原因是,所有可以表示为不重复小数的数字都可以用小数类型精确表示。现实世界中的货币单位始终是不重复的小数。正如其他人所说,您的问题是,由于某种原因,您的价格不能表示为不重复的小数。就是这样0.083333333...。使用双精度数实际上对准确性没有帮助——双精度数也不能准确地表示 1/12。在这种情况下,缺乏准确性不会造成问题,但在其他情况下可能会造成问题。
更重要的是,选择使用双精度数意味着还有更多的数字无法完全准确地表示。例如 0.01、0.02、0.03... 是的,您可能关心的很多数字都无法准确地表示为双精度数。
在这种情况下,价格从何而来的问题确实很重要。无论您将价格存储在何处,几乎可以肯定存储的价格并不1/12准确。要么您已经存储了近似值,要么该价格实际上是计算的结果(或者您正在使用一个非常不寻常的数字存储系统,您在其中存储有理数,但这似乎不太可能)。
您真正想要的是一个可以表示为双精度的价格。如果这就是您所拥有的,但随后您对其进行了修改(例如,通过除以 12 来从年度中获得每月成本),那么您需要尽可能晚地进行该除法。而且很可能您还需要计算每月费用作为未清余额的除法。我最后一部分的意思是,如果您每年每月分期付款 10 美元,那么第一个月可能会收取 0.83 美元。然后第二个月您收取 ($10-0.83)/11。这又会是 0.83。在第五个月,您收取 (10-0.83*4)/8,现在是 0.84(四舍五入后)。然后下个月是 (10-0.83*4-0.84)/7 等等。这样您就可以保证总费用正确,并且不用担心复合错误。
归根结底,您是唯一一个可以判断是否可以重新构建系统以消除此类所有舍入错误的人,或者是否必须按照我的建议以某种方式减轻它们的人。不过,最好的选择是阅读有关浮点数(十进制和二进制)的所有内容,以便充分理解选择其中一种的含义。
| 归档时间: |
|
| 查看次数: |
3615 次 |
| 最近记录: |