首页 文章

我什么时候应该使用double而不是decimal?

提问于
浏览
243

我可以说使用 double (或 float )而不是 decimal 有三个好处:

  • 使用较少的内存 .

  • 更快,因为处理器本身支持浮点数学运算 .

  • 可以代表更大范围的数字 .

但这些优势似乎只适用于计算密集型操作,例如建模软件中的操作 . 当然,在需要精确度时,不应使用双精度数,例如财务计算 . 那么在"normal"应用程序中选择 double (或 float )而不是 decimal 是否有任何实际原因?

编辑补充:感谢所有伟大的回应,我向他们学习 .

还有一个问题:一些人认为双打可以更精确地代表实数 . 宣布时,我认为他们通常也会更准确地代表他们 . 但是,当执行浮点运算时,准确度可能会降低(有时是显着的)吗?

12 回答

  • 54

    我认为你已经很好地总结了这些优势 . 但是你缺少一点 . decimal类型在表示基数为10的数字时更准确(例如用于货币/财务计算的数字) . 一般来说,double类型将提供至少同样高的精度(如果我错了,有人会纠正我),并且对任意实数来说肯定会提高速度 . 简单的结论是:在考虑使用哪个时,除非您需要 decimal 提供的 base 10 精度,否则请始终使用 double .

    Edit:

    关于操作后浮点数准确性降低的其他问题,这是一个稍微微妙的问题 . 实际上,精确度(我在这里使用术语可互换以获得准确性)将在每次操作执行后稳步下降 . 这是由于两个原因:

    • 某些数字(最明显的小数)不能以浮点形式真正表示的事实
      发生
    • 舍入错误,就像您手动执行计算一样 . 它在很大程度上取决于上下文(您正在执行的操作数量),这些错误是否足够重要,无论如何都要多加考虑 .

    在所有情况下,如果你想比较理论上应该相等的两个浮点数(但是使用不同的计算得出),你需要允许一定程度的容差(多少变化,但通常非常小) .

    有关可以引入精度误差的特定情况的更详细概述,请参阅Wikipedia article的准确性部分 . 最后,如果您想要在机器级别对浮点数/操作进行认真深入(和数学)的讨论,请尝试阅读经常引用的文章What Every Computer Scientist Should Know About Floating-Point Arithmetic .

  • 23

    您似乎可以看到使用浮点类型的好处 . 我倾向于在所有情况下设计小数,并依靠分析器让我知道十进制操作是否导致瓶颈或减速 . 在这些情况下,我将“向下转换”为double或float,但仅在内部执行,并通过限制正在执行的数学运算中的有效位数来小心地尝试管理精度损失 .

    通常,如果您的值是瞬态的(不重用),则可以安全地使用浮点类型 . 浮点类型的真正问题是以下三种情况 .

    • 您正在聚合浮点值(在这种情况下,精度错误复合)

    • 您基于浮点值构建值(例如,在递归算法中)

    • 您正在使用大量有效数字进行数学运算(例如, 123456789.1 * .000000000000000987654321

    EDIT

    根据reference documentation on C# decimals

    decimal关键字表示128位数据类型 . 与浮点类型相比,十进制类型具有更高的精度和更小的范围,这使其适用于财务和货币计算 .

    所以澄清我的上述说法:

    我倾向于在所有情况下设计小数,并依靠分析器让我知道十进制操作是否导致瓶颈或减速 .

    我只在小数有利的行业工作过 . 如果你正在研究phsyics或图形引擎,那么设计浮点类型(float或double)可能更有利 .

    十进制不是无限精确的(在原始数据类型中不可能表示非整数的无限精度),但它比双精度要精确得多:

    • decimal = 28-29有效数字

    • double = 15-16位有效数字

    • float = 7位有效数字

    EDIT 2

    在回应Konrad Rudolph的评论时,第1项(上文)肯定是正确的 . 不精确的聚合确实复杂化 . 请参阅以下代码以获取示例:

    private const float THREE_FIFTHS = 3f / 5f;
    private const int ONE_MILLION = 1000000;
    
    public static void Main(string[] args)
    {
        Console.WriteLine("Three Fifths: {0}", THREE_FIFTHS.ToString("F10"));
        float asSingle = 0f;
        double asDouble = 0d;
        decimal asDecimal = 0M;
    
        for (int i = 0; i < ONE_MILLION; i++)
        {
            asSingle += THREE_FIFTHS;
            asDouble += THREE_FIFTHS;
            asDecimal += (decimal) THREE_FIFTHS;
        }
        Console.WriteLine("Six Hundred Thousand: {0:F10}", THREE_FIFTHS * ONE_MILLION);
        Console.WriteLine("Single: {0}", asSingle.ToString("F10"));
        Console.WriteLine("Double: {0}", asDouble.ToString("F10"));
        Console.WriteLine("Decimal: {0}", asDecimal.ToString("F10"));
        Console.ReadLine();
    }
    

    这输出如下:

    Three Fifths: 0.6000000000
    Six Hundred Thousand: 600000.0000000000
    Single: 599093.4000000000
    Double: 599999.9999886850
    Decimal: 600000.0000000000
    

    正如您所看到的,即使我们使用相同的源常量添加,double的结果也不太精确(尽管可能会正确地舍入),并且浮点数远不那么精确,直到它已经减少到只有两位有效数字 .

  • 6

    使用十进制表示基数为10的值,例如其他人建议的财务计算 .

    但对于任意计算值,double通常更准确 .

    例如,如果要计算投资组合中每一行的权重,请使用double,因为结果将更接近100% .

    在以下示例中,doubleResult比decimalResult更接近1:

    // Add one third + one third + one third with decimal
    decimal decimalValue = 1M / 3M;
    decimal decimalResult = decimalValue + decimalValue + decimalValue;
    // Add one third + one third + one third with double
    double doubleValue = 1D / 3D;
    double doubleResult = doubleValue + doubleValue + doubleValue;
    

    再次以组合为例:

    • 投资组合中每一行的市场 Value 是货币 Value ,可能最好用十进制表示 .

    • 投资组合中每条线的权重(=市场 Value / SUM(市场 Value ))通常更好地表示为双倍 .

  • 0

    当你不需要精确度时,使用double或float,例如,在我写的平台游戏中,我使用浮动来存储玩家的速度 . 显然我在这里不需要超精确,因为我最终会绕过Int来绘制屏幕 .

  • 2

    在某些会计中,请考虑使用整数类型或结合使用整数类型的可能性 . 例如,假设您运行的规则要求每个计算结果至少带有6个小数位,最终结果将四舍五入到最接近的便士 .

    计算100美元的1/6会产生$ 16.66666666666666 ...,因此工作表中的 Value 为16.666667美元 . double和decimal都应该准确地将结果产生到6位小数 . 但是,我们可以通过将结果转发为整数16666667来避免任何累积错误 . 每个后续计算都可以以相同的精度进行并类似地进行 . 继续这个例子,我计算了该金额的德克萨斯州销售税(16666667 * .0825 = 1375000) . 添加两个(这是一个简短的工作表)1666667 1375000 = 18041667.将小数点移回给我们18.041667,或18.04美元 .

    虽然这个简短的例子不会产生使用double或decimal的累积误差,但是很容易显示简单地计算double或decimal并且继续前进会累积重大错误的情况 . 如果您在其下运行的规则需要有限的小数位数,则将每个值存储为整数乘以10 ^(所需的小数位数),然后除以10 ^(所需的小数位数)以获得实际值值将避免任何累积错误 .

    在没有发生几分钱的情况下(例如,自动售货机),根本没有理由使用非整数类型 . 简单地把它想象成便士,而不是美元 . 我看过代码,其中每个计算只涉及整个便士,但使用双重导致错误!仅整数数学删除了该问题 . 所以我的非常规答案是,如果可能的话,放弃双倍和小数 .

  • 0

    如果您需要与其他语言或平台进行二进制交互,那么您可能需要使用标准化的float或double .

  • 0

    注意:这篇文章是基于http://csharpindepth.com/Articles/General/Decimal.aspx的十进制类型功能的信息以及我自己对这意味着什么的解释 . 我将假设Double是正常的IEEE双精度 .

    注2:这个帖子中最小和最大的数量是这个数字的大小 .

    “十进制”的优点 .

    • "decimal"可以准确表示可以写为(足够短)小数的数字,double不能 . 这在财务分类账中非常重要,类似的是,结果与计算人员所做的完全匹配非常重要 .

    • "decimal"的尾数比"double"大得多 . 这意味着对于其范围内的值,标准化范围"decimal"将具有比双精度高得多的精度 .

    小数的缺点

    • 它会慢得多(我没有基准,但我猜测至少可能有一个数量级),十进制不会受益于任何硬件加速和算术就需要相对昂贵的乘法/除法10 (这比2的幂乘法和乘法要贵得多)在加法/减法之前匹配指数,并且在乘法/除法之后将指数带回范围 .

    • 十进制将提前溢出双重意志 . 十进制只能表示高达±296-1的数字 . 通过比较,double可以表示高达近±21024的数字

    • 十进制将提前下溢 . 以十进制表示的最小数字是±10-28 . 通过比较,如果支持次要数字,则double可以表示低至2-149(约10-45)的值,如果不支持,则可以表示2-126(约10-38) .

    • 十进制占用的内存是double的两倍 .

    我的意见是你应该默认使用“十进制”进行金钱工作和其他情况,其中匹配人类计算确实很重要,你应该使用use double作为你的默认选择剩下的时间 .

  • 291

    如果您重视性能而不是正确性,请使用浮点数 .

  • 4

    选择应用程序的功能类型 . 如果您需要财务分析中的精确度,您已回答了您的问题 . 但是如果你的应用程序可以通过估算你的确定加倍 .

    您的申请是否需要快速计算,或者他是否会一直在世界上给您答案?这实际上取决于应用程序的类型 .

    图形饿了吗?漂浮或双倍就足够了 . 财务数据分析,流星撞击行星的那种精确度?那些需要一点精度:)

  • 0

    Decimal有更宽的字节,CPU本身支持double . 十进制是基数为10,因此在计算小数时会发生十进制到双精度转换 .

    For accounting - decimal
    For finance - double
    For heavy computation - double
    

    请记住,.NET CLR仅支持Math.Pow(double,double) . 不支持十进制 .

    .NET Framework 4

    [SecuritySafeCritical]
    public static extern double Pow(double x, double y);
    
  • 0

    如果该表示法比十进制显示短,则双值将默认序列化为科学记数法 . (例如.00000003将是3e-8)十进制值永远不会序列化为科学记数法 . 当序列化供外部用户使用时,这可能是一个考虑因素 .

  • 3

    取决于您的需求 .

    因为float和double是二进制数据类型,所以在舍入数字的方式中有 some diifculties和errrors,因此例如double将舍入0.1到0.100000001490116,double也将舍入1/3到0.33333334326441 . 简单地说,并非所有实数都具有双重类型的准确表示

    幸运的是,C#还支持所谓的十进制浮点运算,其中数字通过十进制数字系统而不是二进制系统表示 . 因此,十进制浮点运算 does not lose accuracy 在存储和处理浮点数时 . 这使得它非常适合需要高精度的计算 .

相关问题