我们正在进行财务计算.我发现这篇关于存储货币值为小数的帖子:十进制vs双! - 我应该使用哪一个?何时使用?
所以我将金额存储为小数.
我有以下计算:12.000*(1/12)= 1.000
如果我使用十进制数据类型来存储金额和结果金额,我得不到预期的结果
// First approach:
decimal ratio = 1m / 12m;
decimal amount = 12000;
decimal ratioAmount = amount * ratio;
ratioAmount = 999.9999999999999
// Second approach:
double ratio = 1d / 12d;
decimal amount = 12000;
decimal ratioAmount = (decimal)((double)amount * ratio);
ratioAmount = 1.000
// Third approach:
double ratio = 1d / 12d;
double amount = 12000;
double ratioAmount = amount * ratio;
ratioAmount = 1.000
Run Code Online (Sandbox Code Playgroud)
什么是最好的方法?每个人都在谈论金额/金钱必须存储为小数.
似乎所有这些帖子都很接近,但并没有完全解释问题的症结所在。并不是decimal更精确地存储值或double具有更多数字或类似的东西。它们各自以不同的方式存储值。
该decimal类型以十进制形式存储值。喜欢1234.567。的double(和float在一个二进制形式)存储值,比如1101010.0011001。(他们也对可以存储的数字数量有限制,但这在此处无关紧要 - 或永远无关。如果您觉得为了精确而用完数字,您可能做错了)
请注意,某些值无法以任一表示法精确存储,因为它们需要小数点后无限数量的数字。喜欢1/3或1/12。这些值在存储时会四舍五入,这就是您在这里看到的。
decimal在金融计算中的优点是它可以精确地存储小数而double不能。例如,0.1可以精确地存储在 中,decimal但不能存储在中double。这些是货币金额通常采用的价值类型。你永远不需要存储 2/3 美元,你正好需要 0.66 美元。人类货币是基于十进制的,因此该decimal类型可以很好地存储它们。
此外,加减十进制值也适用于该decimal类型。这是财务计算中最常见的操作,因此以这种方式编程更容易。
乘以十进制值也很有效,尽管它可以增加用于确保精确值的小数位数。
但是除法是非常危险的,因为通过除法获得的大多数值都无法精确存储,并且会发生舍入错误。
在一天结束的时候都double和decimal可以用于存储货币值,你只需要非常小心自己的局限性。对于一种double类型,您需要在每次计算后对结果进行四舍五入,甚至是加法和减法。每当您向用户显示值时,您都需要明确地将它们格式化为具有一定数量的十进制数字。此外,在比较数字时,请注意仅比较前 X 位十进制数字(通常为 2 或 4)。
对于某种decimal类型,其中一些限制可以放宽,因为您知道您的货币价值是精确存储的。您通常可以在加法和减法之后跳过舍入。如果首先只存储 X 个十进制数字,则无需担心显式显示格式和比较。它确实使事情变得容易得多。但是你仍然需要在乘法和除法之后四舍五入。
这里没有讨论一种更优雅的方法。更改您的货币单位。不是存储美元值,而是存储美分值。或者,如果您使用 4 个十进制数字,则存储 1/100 分。
然后你可以使用int或long为一切!
这与 a 具有大部分相同的优点decimal(值精确存储,加/减工作精确),但您需要舍入的地方将变得更加明显。然而,一个轻微的缺点是格式化这些用于显示的值变得有点复杂。另一方面,如果您忘记这样做,那也是显而易见的。到目前为止,这是我的首选方法。
永远不会永远存储金融金额.这是我博客中的一个示例,说明了为什么double不应该使用:
var lineValues = new List<double> { 1675.89, 2600.21, 5879.79, 5367.51, 8090.30, 492.97, 7888.60 };
double dblAvailable = 31995.27d;
double dblTotal = 0d;
foreach (var lineValue in lineValues)
{
dblTotal += lineValue;
}
if (dblAvailable < dblTotal)
{
Console.WriteLine("They don't add up!");
}
Run Code Online (Sandbox Code Playgroud)
你会看到它Console.WriteLine会被击中,因为双打实际上加起来是31995.270000000004.正如您可以从变量名称中猜测的那样,此代码示例基于财务系统中的一些实际代码 - 此问题导致用户无法正确地为交易分配金额.
decimal使用以下附加代码将数字添加为s:
decimal decAvailable = (decimal)dblAvailable;
decimal decTotal = (decimal)dblTotal;
if (decAvailable < decTotal)
{
Console.WriteLine("They still don't add up!");
}
Run Code Online (Sandbox Code Playgroud)
不会打的Console.WriteLine.故事的寓意:decimal用于财务计算!
decimal关键字的语言参考的第一部分说明:
与其他浮点类型相比,该
decimal类型具有更高的精度和更小的范围,这使其适用于财务和货币计算.
这也是值得注意的,对于一个数字文本为十进制来处理,后缀m(为米 oney)应使用,对财务数据的类型适当性进一步指出.
| 归档时间: |
|
| 查看次数: |
440 次 |
| 最近记录: |