How to represent currency or money in C
TL;DR1用C表示货币或货币的准确和可维护的方法是什么?
问题背景:许多其他语言都回答了这个问题,但我找不到C语言的可靠答案。
C我应该使用什么数据类型来表示C中的货币?
为什么不使用双元或浮点来表示货币?
目标C如何在目标C/IOS中表示资金?
注意:对于其他语言,还有很多类似的问题,我只是为了表示的目的拉了几个问题。
所有这些问题都可以归结为"使用
有一个相关的问题最终建议使用"定点"方法,但没有一个答案使用C中的特定数据类型。
同样,我也研究过任意精度库,比如gmp,但我不清楚这是否是最好的使用方法。
简化假设:
假设一个基于x86或X64的体系结构,但是请说出任何可能影响基于RISC的体系结构的假设,例如电源芯片或ARM芯片。
计算的准确性是首要要求。下一个要求是易于维护。计算速度很重要,但却是其他要求的第三级。
计算需要能够安全地支持精确到磨机的操作,以及支持高达万亿(10^9)的值。
与其他问题的区别:
如上所述,这种类型的问题之前已经被多个其他语言问过。这个问题与其他问题有两个不同的原因。
使用接受的答案:为什么不使用double或float来表示货币?,让我们突出差异。
(Solution 1) A solution that works in just about any language is to use integers instead, and count cents. For instance, 1025 would be $10.25. Several languages also have built-in types to deal with money. (Solution 2) Among others, Java has the BigDecimal class, and C# has the decimal type.
emphasis added to highlight the two suggested solutions
第一个解决方案本质上是"定点"方法的变体。该解决方案存在一个问题,即建议的范围(跟踪美分)不足以进行基于工厂的计算,并且在舍入时会丢失重要信息。
另一种解决方案是使用C中不可用的本地
同样,答案不考虑其他选项,例如创建用于处理这些计算的结构或使用任意精度库。这些差别是可以理解的,因为Java没有结构,当语言中有本地支持时,为什么要考虑第三方库。
这个问题不同于那个问题和其他相关的问题,因为C没有与其他语言相同级别的本机类型支持,并且具有其他语言不具备的语言功能。我还没有看到其他任何问题解决了C中解决这个问题的多种方法。
问题:根据我的研究,由于浮点错误,
我应该用什么来表示C中的钱,为什么这种方法比其他方法更好?
1此问题以较短的形式开始,但收到的反馈表明需要澄清问题。
使用整数数据类型(long long、long、int)或BCD(二进制编码十进制)算术库。您应该存储将要显示的最小数量的十分之一或百分之一。也就是说,如果您使用美元并表示美分(百分之一美元),则您的数值应为表示mill或millrays(十分之一或百分之一美分)的整数。额外的重要数字将确保您的兴趣和类似的计算一致。
如果使用整数类型,请确保它的范围足够大,可以处理大量的问题。
最好的货币/货币表示法是使用更高精度的浮点类型,如
四种可选方案:整数、非十进制浮点、特殊十进制浮点、用户定义的结构。
整数:一个通用的解决方案使用最小面额的整数计数。例如,计算美元而不是美元。整数的范围需要合理的宽。像
二元浮点:2个常见的陷阱:1)使用
1 2 3 4 | // Sample - does not properly meet nuanced corner cases. double RoundToNearestCents(double dollar) { return round(dollar * 100.0)/100.0; } |
一些系统提供了一个"十进制"类型,而不是
当然,用户定义的结构(比如固定点)可以解决所有问题,除了代码太多容易出错和工作(相反)之外。结果可能功能完美,但缺乏性能。
结论:这是一个深刻的主题,每一种方法都值得更广泛的讨论。一般的答案是:没有一般的解决方案,因为所有的方法都有明显的弱点。所以这取决于应用程序的具体情况。
[编辑]考虑到OP的额外编辑,建议使用最小货币单位的
1 2 | double interest_in_cents = round( Monthly_payment(0.07/12 /* percent */, N_payments, principal_in_cents)); |
我的水晶球说,到2022年,美国将下降0.01美元,最小的单位将是0.05美元。我会使用最能处理这种转变的方法。
如果速度是你最关心的问题,那么使用一个积分型的单位,你需要表示的最小单位(如一个工厂,这是0.001美元或0.1美分)。因此,
这种方法的问题是可能会用完数字;32位无符号int可以表示10位十进制数字,因此在这种方案下可以表示的最大值是
另一种方法是使用一个结构类型,其中一个整数成员表示整美元金额,另一个整数成员表示小数美元金额(同样,缩放到需要表示的最小单位,无论是美分、米尔斯或更小的单位),类似于
1 2 3 4 | struct money { long whole_dollars; // long long if you have it and you need it int frac_dollar; }; |
一个
如果您更担心存储任意大的值,那么总是有bcd,它可以表示比任何本机整数或浮点类型多得多的数字。
不过,表示法只占战斗的一半;您还必须能够对这些类型执行算术运算,并且对货币的操作可能有非常具体的舍入规则。所以在决定你的代理权时,你要考虑到这一点。
在进行计算时,你必须注意一些问题,这样你就不会把有效数的一半分开。
这是一个知道范围的游戏,当除法后的四舍五入很好的时候。
例如,在除法之后做一个适当的取整(0.5以上的变量),可以先将分子的一半加到值上,然后再进行除法。不过,如果你在做财务工作,你需要一个更先进的轮值制,尽管你的会计师会批准。
1 | long long res = (amount * interest + 500)/1000; |
仅在与用户通信时转换为美元(或任何其他货币)。