Decimal vs Double

Decimal vs Double Speed

我编写财务应用程序,在那里我经常与使用双精度与使用小数的决定作斗争。

我所有的数学运算都是针对小数点后不超过5位且不超过100000的数字。我有一种感觉,所有这些都可以表示为双精度,没有舍入误差,但从来没有确定。

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

问题:

  • 速度优势真的像幼稚的测试所显示的那么大吗?(100次)
  • 有没有办法保证ToString的输出是我想要的?我的号码总是可以代表的,这能保证吗?
  • 更新:在我的应用程序运行之前,我必须处理大约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,还可以尝试使用int64_t64位整数类型的缩放整数算法。哪个更快将取决于您的硬件平台。


    任何财务计算总是使用小数点,否则您将永远追求1%的舍入误差。


  • 是的;软件运算真的比硬件慢100倍。或者,至少,它慢得多,一个100的因子,给或取一个数量级,大概是正确的。回到过去那些糟糕的日子,当你不能假设每个80386都有一个80387浮点协处理器时,那么你也有了二进制浮点的软件模拟,这很慢。
  • 不,如果你认为一个纯二进制浮点数可以精确地表示所有的十进制数,那你就生活在一个幻想的世界里。二进制数可以合并为半、四分之一、八分之一等,但由于精确的十进制为0.01需要两个五分之一和一个四分之一的因子(1/100=(1/4)*(1/5)*(1/5)),并且由于五分之一没有精确的二进制表示,所以不能用二进制值精确地表示所有的十进制值(因为0.01是一个反例,它是ICH不能精确表示,但它代表了一大类无法精确表示的十进制数)。
  • 因此,在调用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);

    这将打印出真的!

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


    我请你参考我对这个问题的回答。

    使用长的,存储所需跟踪的最小数量,并相应地显示值。