十进制与双速

时间:2008-11-30 23:42:32

标签: double decimal finance

我写财务应用程序,我经常决定使用双倍与使用小数。

我的所有数学都适用于小数不超过5位且不大于~100,000的数字。我觉得所有这些都可以表示为双打而不会出现舍入错误,但是从来没有确定过。

我会继续从小数转换为双倍以获得明显的速度优势,除了在一天结束时,我仍然使用ToString方法将价格传输到交易所,并且需要确保它始终输出我期望的数字。 (89.99而不是89.99000000001)

问题:

  1. 速度优势真的和天真的测试一样大吗? (~100次)
  2. 有没有办法保证ToString的输出成为我想要的?我的号码总是可以表示的吗?
  3. 更新:我必须在我的应用程序运行之前处理大约100亿次价格更新,并且我已经实现了带有小数的明显保护原因,但是只需要约3个小时才能打开,双打会大大减少我的开启时间。有没有一种安全的方法来做双打?

7 个答案:

答案 0 :(得分:79)

  1. 浮点运算几乎总是快得多,因为它直接由硬件支持。到目前为止,几乎没有广泛使用的硬件支持十进制算术(虽然这正在改变,请参阅注释)。
  2. 财务应用程序应始终使用十进制数字,在财务应用程序中使用浮点所产生的恐怖故事数量是无穷无尽的,您应该能够通过Google搜索找到许多此类示例。
  3. 虽然十进制算术可能比浮点算术慢得多,但除非您花费大量时间处理十进制数据,否则对程序的影响可能会微不足道。与往常一样,在开始担心差异之前,请进行适当的分析。

答案 1 :(得分:23)

这里有两个可分开的问题。一个是双精度是否具有足够的精度来容纳你需要的所有位,另一个是它可以准确地代表你的数字。

至于确切的表示,你是谨慎的,因为像1/10这样的精确小数没有确切的二进制对应。但是,如果您知道只需要5个十进制数字的精度,则可以使用缩放算术运算数字乘以10 ^ 5。因此,例如,如果您想要完全代表23.7205,则将其表示为2372050。

让我们看看是否有足够的精度:双精度为您​​提供53位精度。 这相当于精度的15+十进制数字。因此,这将允许您在小数点后五位数和小数点前10位数,这似乎适合您的应用程序。

我会把这个C代码放在.h文件中:

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,还可以使用int64_t 64位整数类型尝试缩放整数运算。哪个更快将取决于您的硬件平台。

答案 2 :(得分:20)

始终使用十进制进行任何财务计算,否则您将永远追逐1个舍入错误。

答案 3 :(得分:12)

  1. 是;软件算法确实比硬件慢100倍。或者,至少,它是慢得多,并且100倍,给出或采取一个数量级,是正确的。回到过去,你不能假设每个80386都有一个80387浮点协处理器,那么你也有二进制浮点的软件模拟,这很慢。
  2. 否;如果您认为纯二进制浮点数可以精确地表示所有十进制数,那么您就生活在幻想世界中。二进制数可以合并一半,四分之一,八分之一等,但由于0.01的精确小数要求两个因子为五分之一和一个四分之一(1/100 =(1/4)*(1/5)*(1 / 5))因为五分之一没有精确的二进制表示,你不能用二进制值精确地表示所有十进制值(因为0.01是一个反例,它不能完全表示,但代表了一大类十进制数,无法准确表示。)
  3. 因此,您必须在调用ToString()之前决定是否可以处理舍入,或者是否需要找到一些其他机制来处理将结果转换为字符串时舍入结果。或者你可以继续使用十进制算术,因为它将保持准确,并且一旦发布了支持硬件中新的IEEE 754十进制算术的机器,它将变得更快。

    强制性交叉引用:What Every Computer Scientist Should Know About Floating-Point Arithmetic。这是许多可能的URL之一。

    有关十进制算术的信息以及此Speleotrove站点的新IEEE 754:2008标准。

答案 4 :(得分:8)

只需使用长并乘以10的幂。完成后,除以10的相同幂。

答案 5 :(得分:5)

小数应始终用于财务计算。数字的大小并不重要。

我解释的最简单方法是通过一些C#代码。

double one = 3.05;
double two = 0.05;

System.Console.WriteLine((one + two) == 3.1);

即使3.1等于3.1,该位代码也将打印出 False ...

同样的事情......但是使用十进制:

decimal one = 3.05m;
decimal two = 0.05m;

System.Console.WriteLine((one + two) == 3.1m);

现在打印出 True

如果你想避免这类问题,我建议你坚持使用小数。

答案 6 :(得分:1)

我建议你回答this question给我的答案。

使用long,存储您需要跟踪的最小量,并相应地显示值。