Decimal vs Double Speed
我编写财务应用程序,在那里我经常与使用双精度与使用小数的决定作斗争。
我所有的数学运算都是针对小数点后不超过5位且不超过100000的数字。我有一种感觉,所有这些都可以表示为双精度,没有舍入误差,但从来没有确定。
为了明显的速度优势,我会继续进行从小数到双数的转换,除了在一天结束时,我仍然使用ToString方法将价格传输到交易所,并且需要确保它始终输出我期望的数字。(89.99而不是89.9900000001)
问题:
更新:在我的应用程序运行之前,我必须处理大约100亿次的价格更新,由于明显的保护原因,我现在已经用十进制实现了,但是仅仅打开它需要大约3个小时,双打会大大减少我的打开时间。双打有安全的方法吗?
这里有两个可分离的问题。一个是double是否具有足够的精度来容纳您需要的所有位,另一个是它可以准确地表示您的数字。
至于精确的表示法,您应该谨慎,因为像1/10这样的精确小数没有精确的二进制对应。但是,如果您知道您只需要5位小数的精度,那么您可以使用缩放算法,在其中对乘以10^5的数字进行运算。因此,例如,如果您想要表示23.7205,那么您将它精确地表示为2372050。
让我们看看是否有足够的精度:双精度提供53位的精度。这相当于精度的15+位小数。所以这将允许您在小数点后五位数字,在小数点前十位数字,这对于您的应用程序来说是足够的。
我将把这个C代码放在一个.h文件中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | typedef double scaled_int; #define SCALE_FACTOR 1.0e5 /* number of digits needed after decimal point */ static inline scaled_int adds(scaled_int x, scaled_int y) { return x + y; } static inline scaled_int muls(scaled_int x, scaled_int y) { return x * y / SCALE_FACTOR; } static inline scaled_int scaled_of_int(int x) { return (scaled_int) x * SCALE_FACTOR; } static inline int intpart_of_scaled(scaled_int x) { return floor(x / SCALE_FACTOR); } static inline int fraction_of_scaled(scaled_int x) { return x - SCALE_FACTOR * intpart_of_scaled(x); } void fprint_scaled(FILE *out, scaled_int x) { fprintf(out,"%d.%05d", intpart_of_scaled(x), fraction_of_scaled(x)); } |
可能有一些粗糙的地方,但这应该足以让你开始。
没有额外的开销,乘或除的成本加倍。
如果您可以访问c99,还可以尝试使用
任何财务计算总是使用小数点,否则您将永远追求1%的舍入误差。
因此,在调用ToString()之前,您必须决定是否可以处理舍入,或者是否需要找到其他机制来处理将结果转换为字符串时的舍入。或者,您可以继续使用十进制算法,因为它将保持精确,并且一旦发布支持新的IEEE754硬件十进制算法的机器,它将变得更快。
强制性交叉参考:每个计算机科学家都应该知道的关于浮点运算的知识。这是许多可能的网址之一。
有关十进制算术和新的IEEE754:2008标准的信息,请访问此speleotrove网站。
只要用一个长的数乘以10的幂。完成后,除以10的相同幂。
财务计算应始终使用小数。数字的大小并不重要。
对我来说,最简单的解释方法是通过一些C代码。
1 2 3 4 | double one = 3.05; double two = 0.05; System.Console.WriteLine((one + two) == 3.1); |
即使3.1等于3.1,该位代码也将打印为假。
同一件事…但使用十进制:
1 2 3 4 | decimal one = 3.05m; decimal two = 0.05m; System.Console.WriteLine((one + two) == 3.1m); |
这将打印出真的!
如果你想避免这类问题,我建议你坚持使用小数。
我请你参考我对这个问题的回答。
使用长的,存储所需跟踪的最小数量,并相应地显示值。