2017-02-21 81 views
-1

我支持金融.net应用程序。对于财务资料,使用十进制数据类型有很多建议。小数点再次翻倍

现在,我坚持这一点:

decimal price = 1.0m/12.0m; 
decimal quantity = 2637.18m; 
decimal result = price * quantity; //results in 219.76499999999999999999999991 

的问题是,正确的价值,我们的客户收费是219.77(圆形功能,MidpointRounding.AwayFromZero),而不是219.76。

如果我改变一切翻一番,似乎工作:

double price = 1.0/12.0; 
double quantity = 2637.18; 
double result = price * quantity; //results in 219.765 

要我改变一切翻番?分数会有其他问题吗?

我觉得这个问题是来自Difference between decimal, float and double in .NET?不同,因为它并不真的比与数据类型的结果向我解释为什么小数将用更精确的数据类型的结果是不准确的(样品上面),这使用较少的字节。

+2

可能重复[在十进制,浮点数和浮点数之间的差异?](http://stackoverflow.com/questions/618535/difference-between-decimal-float-and-double-in-net) –

+7

只是计算中的一个基本缺陷,1/12从来没有一个完美的结果,除非你用12个手指数。什么样的公司向客户收取一件物品的十二分之一美元?你必须首先得到单价。 –

+1

“我应该改变一切” - 为什么?你发现了一个例子,你的期望没有得到满足(尽管如汉斯所指出的,你的单位价格不容易代表奇怪的起点,然后我会指出它不是一个单位价格,因为你显然可以细分你的单位)。选择数量更好的*表示*会更好。如果不是这样,有可能找到平等和相反的例子,其中'decimal'适合和'double'产生错误的结果。 –

回答

4

推荐十进制的原因是所有可以表示为非重复小数的数字都可以用十进制精确表示。现实世界中的货币单位总是不重复的小数。其他人所说的问题是,由于某种原因,您的价格不能用非重复小数表示。那是0.083333333...。使用双精度对准确度没有实际帮助 - 双精度不能准确地表示1/12。在这种情况下,缺乏准确性不会导致问题,但在其他情况下可能会出现问题。

此外更重要的是,选择使用double将意味着还有更多的数字无法完全准确地表示。例如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等等。这样你保证总费用是正确的,不用担心复合错误。

在一天结束时,您是唯一一个判断您是否可以重新构建系统来删除所有这种舍入错误的方法,或者您是否按照我的建议以某种方式缓解了这些错误。你最好的选择是阅读关于浮点数的所有事情,包括小数和二进制,这样你就可以完全理解选择浮点数的意义了。

1

通常情况下,在财务计算中,乘法和除法预计会以某种方式四舍五入到一定的小数位数。 (大多数货币系统只使用基数10的金额;在这些系统中,非基数10的金钱是罕见的,如果它们曾经发生过。)将价格除以12并不总是期望产生基数10号码;业务逻辑将决定该价格将如何四舍五入,包括结果的小数位数。根据业务逻辑的不同,0.083333333333333333等结果可能并不合适。