2015-08-25 69 views
9

TL; DR 什么是用于表示用C的货币或货币准确和维护的方法?如何代表货币或货币使用C


背景的问题:
这已经回答了许多其他语言,但我无法找到C语言的坚实答案。

注意:其他语言还有很多类似的问题,我只是为了表示目的而拉了几个。

所有这些问题都可以归结为“使用decimal数据类型”,其中具体类型可能因语言而异。

有一个related question其最终使用“固定点”的方法暗示,但没有答案的地址中C.使用特定数据类型

同样地,我已经看过任意精度库如GMP ,但是我不清楚这是否是最好的使用方法。


简化的假设:

  • 假定一个基于x86或x64体系结构,但请拨打了会影响基于RISC架构,如电源芯片或手臂的任何假设芯片。

  • 计算的准确性是主要的要求。 Ease of maintenance将是下一个要求。计算速度很重要,但是对其他要求来说是高等的。

  • 计算需要能够安全地支持操作精确到mill以及支持值范围达万亿(10^9)


来自其他问题差异:

如上所述,这种类型的问题之前已经为多个其他语言的要求。由于几个原因,这个问题与其他问题不同。

使用接受的答案:Why not use Double or Float to represent currency?,让我们强调一下差异。

解决方案1 ​​),在几乎任何语言使用整数,而是和伯爵美分有效的解决方案。例如,1025将是10.25美元。几种语言也有内置类型来处理金钱。 (解决方案2)其中,Java有BigDecimal类,C#有十进制类型。

着重强调突出两个建议的解决方案

第一溶液基本上“固定点”的方法的一个变种。这个解决方案存在一个问题,建议的范围(追踪分)不足以用于基于工厂的计算,并且舍入时会丢失重要的信息。

另一解决方案是使用一个本机decimal类,这是不内C.可用

同样,答案不考虑其它选项,例如用于处理这些计算或使用一个任意精度的库创建一个结构。这些都是可以理解的差异,因为Java没有结构,为什么在语言中有本地支持时考虑使用第三方库。

这个问题不同于该问题和其他相关问题,因为C没有相同级别的本机类型支持,并且具有其他语言不支持的语言功能。而且我还没有看到任何其他的问题解决,这可能在C.可以走近多种方式


问题:
从我的研究,似乎float不是由于浮点错误,用于在C程序中表示货币的适当数据类型。

我应该用什么来表示C中的金钱,为什么这种方法比其他方法更好?

这个问题从一个简短的表格开始,但收到的反馈意见表明需要澄清问题。

+4

你总是可以使用美分,并使用'int','long'或'long long'。 – rpsml

+0

你也不想重新发明轮子,因为你很容易就会因为某些事情崩溃而太慢。 – edmz

+1

我不知道是否是最好的方法,但我见过几个人使用由两个整数构成的结构,一个用于单位,另一个用于小数。 – wallek876

回答

2

int(根据需要选择32或64)并根据需要考虑分美分。使用32位并以美分为单位,您可以以单个值表示高达4000万美元。使用64位,它远远超过了所有美国部合并

进行计算时有一些小问题需要注意,因此您不会将有效数字的一半分开。

这是一个知道范围和分割后四舍五入的罚款的游戏。例如,做一个适当的回合(的)。5分钟变化)可以通过首先将一半的分子添加到该值然后进行分割来完成。虽然如果你正在做融资,你需要更高级的轮回系统,虽然你的会计师已经批准了。

long long res = (amount * interest + 500)/1000; 

只有在与用户沟通时才转换为美元(或其他)。

+0

也许'unsigned',除非有负面的钱。 –

+0

@FiddlingBits我的4000万数字是基于无符号的 –

5

可以使用整数数据类型(long long,long,int)或BCD(二进制编码的十进制)算术库。您应该存储您将显示的金额的十分之一或百分之一。也就是说,如果您使用美元并提供美分(百分之一美元),则您的数值应该是代表磨坊或毫米(十分之一或百分之一)的整数。额外的有效数字将确保您的兴趣和类似的计算一致。

如果您使用整数类型,请确保其范围足够大以处理所关注的数量。

3

如果速度是您最关心的问题,那么请使用缩放到您需要代表的最小单位(例如mill,即0.001美元或0.1美分)的整数类型。因此,123456代表$123.456

这种方法的问题是您可能会用完数字;一个32位的无符号整数可以表示10个十进制数字,所以你可以在这个方案下表示的最大值是$9,999,999.999。如果你需要处理数十亿的价值,那就不好。

另一种方法是使用一个结构类型,其中一个整数成员表示整个美元数量,另一个整数成员表示小数美元数量(再次,缩放到您需要表示的最小单位,无论是美分,厂,或者在其他节省整个秒在一个领域,纳秒更小的东西),类似于timeval结构:

struct money { 
    long whole_dollars; // long long if you have it and you need it 
    int frac_dollar; 
};       

int比足够宽处理缩放任何理智的人会使用。如果whole_dollars部分为0,则保留其签名。

如果您更担心存储任意大的值,总是会有BCD,它可以表示比本地整型或浮点型更多的数字。

尽管如此,代表仅仅是战斗的一半;您还必须能够对这些类型执行算术运算,并且货币操作可能会有非常具体的rounding rules。因此,在决定您的表示时,您需要考虑这一点。

+0

'unsigned'比'signed'更合适,不是? –

+1

@FiddlingBits债务? – durron597

+1

@FiddlingBits:你需要能够表示负值,所以你需要一个标志。要么使用成员的签名类型('frac_dollar'需要在'whole_dollars'为'0'的情况下进行签名),或者使它们都没有签名,并使用第三个成员来签名。 –

3

最好的货币/货币表示方法是使用足够高的精度浮点类型,如double,其中包含FLT_RADIX == 10。这些平台/执行者很少见,因为绝大多数系统都有FLT_RADIX == 2

四种备选方案:整数,非十进制浮点数,特殊十进制浮点数,用户定义的结构。

整数:一个常见的解决方案使用所选币种中最小面额的整数计数。以美分而不是美元为例。整数的范围需要合理宽。类似于long long而不是int,因为int只能处理大约+/- 320.00美元。这适用于涉及加/减/多重的简单会计任务,但开始使用利息计算中使用的分部和复杂函数进行破解。 Monthly payment formula。带符号的整数数学没有溢出保护。舍入除法结果时需要注意。 q = (a + b/2)/b不够好。

二进制浮点数:2常见的陷阱:1)使用float这是经常不够精确和2)不正确的舍入。使用double井解决了许多会计限制问题#1。然而,代码仍然经常需要使用所需的最小货币单位来获得令人满意的结果。

// Sample - does not properly meet nuanced corner cases. 
double RoundToNearestCents(double dollar) { 
    return round(dollar * 100.0)/100.0; 
} 

double上甲变化是使用一个double量的最小单位(0.01或0.001)。一个重要的优点是能够简单地通过使用本身符合角落案例的功能进行轮回。

特殊十进制浮点数某些系统提供的double以外的“十进制”类型符合decimal64或类似的内容。虽然这处理了大部分以上的问题,但是可移植性被牺牲了。

当然用户定义的结构(如fixed-point)能解决任何问题,除了它是容易出错代码这么多,它是工作Instead)。结果可能完美,但性能不佳。

结论这是一个深刻的主题,每种方法都应该得到更广泛的讨论。一般的答案是:没有通用的解决方案,因为所有方法都有明显的缺陷。所以这取决于应用程序的具体情况。

[编辑]
鉴于OP的额外修改,建议使用double数货币的最小单位的(例如:$ 0.01 - >double money = 1.0;)。无论何时需要确切值,请在代码的各个点使用round()

double interest_in_cents = round(
    Monthly_payment(0.07/12 /* percent */, N_payments, principal_in_cents)); 

我的水晶球说,到2022年美国将下降至$ 0.01的最小单位为$ 0.05;我会用最能处理这种转变的方法。

+0

也许你的水晶球也预测到加拿大将放弃在2013年发生的硬币。他们在处理现金时(并且只在处理现金时)凑到最接近的5c。尽管如此,你仍然需要计算数学,所以计算没有什么重大变化。 – gilez