TL; DR 1 用C表示货币或金钱的准确且可维护的方法是什么?
问题的背景:
许多其他语言已经回答了这个问题,但我找不到C语言的可靠答案。
Objective-C How to represent money in Objective-C / iOS?
注意:对于其他语言,还有更多类似的问题,我只是为了代表性目的而提取了一些问题。
所有这些问题都可以提炼为"使用decimal
数据类型"特定类型可能因语言而异。
有一个related question最终建议使用"固定点"方法,但没有一个答案使用C中的特定数据类型。
同样,我已经查看了GMP之类的任意精度库,但我不清楚这是否是最好的使用方法。
简化假设:
假定基于x86或x64的架构,但请提及可能影响基于RISC架构的任何假设,例如Power芯片或Arm芯片。
计算的准确性是主要要求。 Ease of maintenance将成为下一个要求。计算速度很重要,但与其他要求相比是次要的。
计算需要能够安全地支持精确到mill的操作以及支持高达数万亿(10 ^ 9)的值
与其他问题的区别:
如上所述,此类问题之前已被问过多种其他语言。由于几个原因,这个问题与其他问题不同。
使用Why not use Double or Float to represent currency?中接受的答案,让我们突出显示差异。
(解决方案1 )几乎可以使用任何语言的解决方案是使用整数,并计算美分。例如,1025将是10.25美元。几种语言也有内置类型来处理钱。 (解决方案2 )其中,Java有BigDecimal类,C#有小数类型。
重点强调了两个建议的解决方案
第一个解决方案基本上是"固定点"的变体。做法。该解决方案存在一个问题,即建议的范围(跟踪分数)不足以进行基于轧机的计算,并且在舍入时将丢失重要信息。
另一个解决方案是使用C语言中没有的本地decimal
类。
同样,答案并未考虑其他选项,例如创建用于处理这些计算的结构或使用任意精度库。这些是可以理解的差异,因为Java没有结构,以及为什么在语言中有本机支持时考虑第三方库。
这个问题与该问题和其他相关问题不同,因为C没有相同级别的本机类型支持,并且具有其他语言不具备的语言功能。我还没有看到任何其他问题解决了在C中可以采用的多种方式。
问题:
根据我的研究,由于浮点错误,似乎float
不适合用于表示C程序中的货币。
我应该用什么来代表C中的钱,为什么这种方法比其他方法更好?
1 此问题以较短的形式开始,但收到的反馈表明需要澄清问题。
答案 0 :(得分:6)
使用整数数据类型(long long,long,int)或BCD(二进制编码的十进制)算术库。您应该存储将显示的最小金额的十分之一或百分之一。也就是说,如果您使用美元并提供美分(百分之一美元),则您的数值应为表示毫克或毫克(十分之一或百分之一分)的整数。额外的重要数字将确保您的兴趣和类似的计算一致。
如果使用整数类型,请确保其范围足以处理关注的数量。
答案 1 :(得分:5)
最佳货币/货币表示方法是使用具有double
的{{1}}更高的精度浮点类型。这些平台/编译器很少见,因为绝大多数系统都有FLT_RADIX == 10
。
四种选择:整数,非十进制浮点,特殊十进制浮点,用户定义结构。
整数:常见解决方案使用所选货币中最小面额的整数计数。计算美分而非美元的示例。整数范围需要合理的范围。像FLT_RADIX == 2
而不是long long
这样的int
只能处理大约+/- $ 320.00。这适用于涉及加/减/多次的简单会计任务,但是开始破解利息计算中使用的除法和复杂函数。 Monthly payment formula。有符号整数数学没有溢出保护。舍入除法结果时需要小心谨慎。 int
不够好。
二进制浮点:2个常见陷阱:1)使用q = (a + b/2)/b
,这通常精度不足,2)舍入不正确。使用float
井解决了许多会计限制的问题#1。然而,代码仍然经常需要使用一轮到期望的最小货币单位才能获得满意的结果。
double
// Sample - does not properly meet nuanced corner cases.
double RoundToNearestCents(double dollar) {
return round(dollar * 100.0)/100.0;
}
的变体是使用double
量的最小单位(0.01或0.001)。一个重要的优点是能够简单地使用double
函数进行舍入,该函数本身可以满足极端情况。
特殊十进制浮点某些系统提供"十进制"符合decimal64或类似内容的round()
以外的其他类型。虽然这可以处理大多数问题,但是牺牲了可移植性。
用户定义的结构(如fixed-point)当然可以解决所有问题,除非它容易出错代码并且 work ({{3 }})。结果可能完美但缺乏性能。
结论这是一个深层次的主题,每种方法都值得进行更广泛的讨论。一般的答案是:没有通用的解决方案,因为所有方法都有明显的缺点。所以它取决于应用程序的具体细节。
[编辑]
鉴于OP的其他修改,建议使用最小单位货币的double
个数字(例如:$ 0.01 - > double
)。每当需要完全值时,在代码的不同位置使用double money = 1.0;
。
round()
我的水晶球说到2022年,美国将降低0.01美元,最小的单位将是0.05美元。我会使用能够最好地处理这种转变的方法。
答案 2 :(得分:4)
如果速度是您最关心的问题,那么请使用缩放到您需要表示的最小单位的整数类型(例如 mill ,即0.001美元或0.1美分)。因此,file:C:/Users/Insanovation/Downloads/F.mp4
代表123456
。
这种方法的问题是你可能会用完数字; 32位无符号整数可以表示10个十进制数字,因此在此方案下可以表示的最大值为$123.456
。如果您需要处理数十亿的价值,那就不好了。
另一种方法是使用具有一个整数成员的结构类型来表示整个美元金额,并使用另一个积分成员来表示分数美元金额(再次,按比例缩放到您需要表示的最小单位,是否' s美分,磨机或更小的东西),类似于$9,999,999.999
结构,可以在一个字段中保存整秒,在另一个字段中保存纳秒:
timeval
struct money {
long whole_dollars; // long long if you have it and you need it
int frac_dollar;
};
更多的宽度足以处理任何理智的人使用的缩放。如果int
部分为0,则保留签名。
如果您更担心存储任意大的值,那么总是BCD,它可以表示比任何本机整数或浮点类型更多的数字。
代表性只有一半的战斗;您还必须能够对这些类型执行算术运算,对货币的操作可能具有非常具体的rounding rules。因此,在决定您的陈述时,您会想要考虑到这一点。
答案 3 :(得分:2)
int
(根据需要为32或64)并根据需要考虑分或分。凭借32位且以美分计算,您可以在单个值中代表高达4000万美元。有了64位,它远远超出了所有美国部门组合。
在进行计算时你必须注意一些问题所以你不要将有效数字的一半分开。
这是一个知道范围的游戏,以及分割后的舍入是好的。
例如,在除法后进行适当的舍入(.5 up variaty)可以通过首先将一半的分子加到该值然后进行除法来完成。虽然如果你正在做财务,你需要更高级的圆形系统,虽然你的会计师已经批准了。
long long res = (amount * interest + 500)/1000;
在与用户沟通时仅转换为美元(或其他)。