【问题标题】:Why is System.Math and for example MathNet.Numerics based on double?为什么 System.Math 和例如 MathNet.Numerics 基于双精度?
【发布时间】:2009-11-05 14:28:21
【问题描述】:

System.Math中的所有方法都以double为参数并返回参数。常量也是double 类型。我查看了MathNet.Numerics,那里的情况似乎也是如此。

这是为什么?特别是对于常量。 decimal 不应该更准确吗?在进行计算时这不是很有用吗?

【问题讨论】:

    标签: c# math double decimal


    【解决方案1】:

    这是一个经典的速度与准确性的权衡。

    但是,请记住,例如,对于 PI,您需要的最多位数是 41。

    pi的最大位数 你永远需要的是 41。 计算周长 误差小于 一个质子的直径,你需要 41 pi † 的数字。看起来很安全 得出结论 41 位就足够了 任何圆的 pi 精度 您可能会遇到的测量问题 遇到。因此,在超过一个 万亿位数的 pi 计算在 2002,第 41 位以后的所有数字都有 没有实用价值。

    此外,十进制和双精度的内部存储结构略有不同。小数被设计用于存储以 10 为底的数据,而作为双精度数(和浮点数)用于保存二进制数据。在二进制机器上(就像现有的每台计算机一样),在存储其范围内的任何数字时,双精度数都会减少浪费的位数。

    同时考虑:

    System.Double 8 字节 约 ±5.0e-324 至 ±1.7e308,有效数字为 15 或 16
    System.Decimal 12 字节 约 ±1.0e-28 至 ±7.9e28,有效数字为 28 或 29

    如您所见,十进制的范围更小,但精度更高。

    【讨论】:

    • 不错的报价!你在哪里找到的?
    • 对 pi 位数的有趣分析,但恰恰相反:如果我想测量的不是围绕宇宙转一圈所需的质子数,而是夸克数需要填满宇宙的体积吗?宇宙正在膨胀,那么我们需要 42 位数字需要多长时间?
    • 在稍微严肃的方面,我认为远少于 41 位数字具有“实用价值”。我想知道任何实际应用程序需要的最多 pi 位数是多少?
    • @Jay:可能是 15 或 16,就像双倍的情况一样。 ;)
    【解决方案2】:

    不,- 小数并不比双精度或任何类型更“精确”。 “精确度”的概念(当谈到计算机中的数字表示时)是错误的。任何类型在表示 一些 数字时都绝对 100% 准确。无符号字节在表示从 0 到 255 的整数时是 100% 准确的。但它们不适用于分数、负数或超出范围的整数。

    小数在表示一组特定的 base 10 值时是 100% 精确的。双精度数(因为它们使用 二进制 IEEE 指数表示来存储它们的值)精确地表示一组 二进制 数字. 一般来说,两者都不比另一个更精确,它们只是用于不同的目的。

    再详细一点,因为我对某些读者来说似乎不够清楚......

    如果你取每一个可以表示为小数的数字,并在数轴上标记它们中的每一个,那么在 个相邻的对之间会有一个额外的 无穷大 个实数, 不能表示为小数。可以对可以表示为双精度数的数字做出完全相同的陈述。如果您将数字行上的每个小数标记为蓝色,并将每个双精度标记为红色,但整数除外,那么在两种颜色中标记相同值的地方将很少。 一般来说,对于 99.99999 % 的分数,(请不要挑剔我的百分比)蓝色组(小数)是与红色组(双打)完全不同的一组数字。

    这是因为根据我们对蓝色集的定义,它是以 10 为底的尾数/指数表示,而双精度数是以 2 为底的尾数/指数表示。任何表示为底数为 2 尾数和指数的值,(1.00110101001 x 2 ^ (-11101001101001) 表示取尾数值 (1.00110101001) 并将其乘以 2 的指数幂(当指数为负时,这相当于除以 2 的幂指数的绝对值)。这意味着在指数为负数的情况下(或尾数的任何部分是小数二进制),该数字不能表示为十进制尾数和指数,反之亦然。

    对于随机落在实数线上的任意实数,它要么更接近蓝色小数之一,要么更接近红色双精度数之一。

    【讨论】:

    • 这不是小杰。这比您需要准确有效地进行浮点数学了解的最低要求。 doubles 的目的是能够快速计算表示误差远小于测量误差的物理量;理解这一点至关重要。
    • @Jay,在数学上,确实有确切的数字。在现实世界中,我们只能以一定的精度“测量”真实事物。然而,一旦有人开始谈论准确性,你所测量的任何东西的实际价值就会发挥作用。 “真实”用户不使用十进制值,他们测量真实的东西。真实的东西不是十进制或二进制的。它们具有真正的价值。无论实际值是什么,最接近的二进制表示或最接近的十进制表示是否更接近实际实际值是一个折腾。
    • @布鲁诺。这根本不是废话。如果你取每个可以表示为小数的数字,那么在它们的每一对相邻的对之间,都有无数个不能表示为小数的实数。双打也是如此。但这些是完全不同的一组数字。对于随机落在实数线上的任意实数,它要么更接近小数之一,要么更接近双数之一。
    • @Charles:我没有说“数学”中没有确切的数字。我说没有“精确测量”。如果你问我,“你的桌面抽屉里有多少个图钉?”我可以给你一个准确的答案,因为这是计数而不是测量。但如果你问我这些图钉有多重,我只能回答一定程度的精确度。更好的量表可以给出更准确的答案。如果我说它们的重量是 232.7 克,更好的秤可能会说它更接近 232.749 克,等等。这显然是不正确的:(继续...)
    • @Charles:“对于随机落在实数线上的任意实数,它要么更接近蓝色小数之一,要么更接近红色双精度数之一。”嗯,其实没有。任何可以精确表示为二进制分数的数字也可以精确表示为十进制分数,但反之则不然。将每个表示为素数的乘积:2=2(很容易),10=2*5。取任意二进制小数,表示为 x/2^n,乘以 5^n/5^n 即可转换为小数。但是如果小数的分子...
    【解决方案3】:

    十进制更精确,但范围更小。您通常会使用 Double 进行物理和数学计算,但您会使用 Decimal 进行财务和货币计算。

    详情请参阅msdn上的以下文章。

    http://msdn.microsoft.com/en-us/library/678hzkk9.aspx

    十进制 http://msdn.microsoft.com/en-us/library/364x0z75.aspx

    【讨论】:

    • -1 因为十进制并不比双精度更精确。这取决于你试图代表什么价值。尝试表示二进制有理数 0.00101101 x 2^(-00011011001)。你会发现双精度可以 100% 准确地表示它,而小数不能。
    • 我将问题中使用的“精确”一词更改为 MSDN 文章中使用的“精确”。精确是表示数字的精度。 Decimal 比 Double 具有更高的精度......
    • 被否决了,但仍然会提出(我承认挑剔)一点,无论精度(位数)如何,值的计算机表示的准确性更多地取决于哪个真实你试图表示的值,而不是你用多少位数来表示它。但是 +1 用于区分物理/数学和金融/货币应用
    • Charles,您确实犯了一个非常常见的错误,即混淆了 precisionaccuracy。一个数字可以非常精确但并不精确:我身高 1.4293859838 米——非常精确,一点也不精确。或者,我身高 1.78 米——一点也不精确,但更准确。这两个数字都不准确。
    • @Eric,如果不知道您的真实身高是多少,则无法评估您的上述陈述...(您是矮个子吗?)但我确实理解其中的区别。如果我说太阳在 1.5782371876433124165413 英寸之外,那是非常精确的,但不是很精确。但是(以我们为例),如果 1.78 实际上是 1.7800000000000000000000000,那么 1.78 可能同样精确。精度“允许”我们获得更高的精度,因为精度越高,相邻“可表示”值之间的空间越小。 “准确度”带来了准确的真实值和表示的值之间的距离。
    【解决方案4】:

    似乎这里关于“它不符合我的要求”的大多数论点都是“但它更快”,ANSI C+Gmp 库也是如此,但没有人提倡这样做吗?

    如果您特别想控制精度,那么还有其他语言会花时间以用户可控的方式实现精确精度:

    http://www.doughellmann.com/PyMOTW/decimal/

    如果精度对您来说真的很重要,那么您最好使用数学家会使用的语言。如果你不喜欢 Fortran,那么 Python 是一个现代的替代品。

    无论您使用哪种语言,请记住以下黄金法则: 避免混合类型... 因此,在尝试使用运算符 b 之前,请将 a 和 b 转换为相同

    【讨论】:

      【解决方案5】:

      如果我冒险猜测,我会说这些函数利用了内部不使用小数的低级数学功能(可能在 C 中),因此返回小数无论如何都需要从 double 转换为 decimal。此外,十进制值类型的目的是保证准确性;这些函数不会也不能在没有无限精度的情况下返回 100% 准确的结果(例如,无理数)。

      【讨论】:

        【解决方案6】:

        如果您需要精确的东西,那么 Decimal、float 或 double 都不够好。此外,十进制是如此昂贵且被过度使用,它已成为一个常见的笑话。

        如果您使用分数并需要极高的精度,请使用分数。这是相同的旧规则,仅在必要时转换一次。您的四舍五入规则也会因应用程序、域等而异,但请确保您可以在合适的地方找到一两个奇怪的示例。但同样,如果您想要分数和最终精度,答案就是不要使用分数以外的任何东西。考虑一下您可能还需要任意精度的功能。

        总的来说,CLR 的实际问题是,实现一个以通用方式处理数字的库非常奇怪和简单,这主要是由于糟糕的原始设计和平台上最流行的编译器的缺点。这与 Java 惨败几乎相同。

        事实证明,double 是涵盖大多数领域的最佳折衷方案,而且效果很好,尽管 MS JIT 仍然无法利用现在大约 15 年历史的 CPU 技术。

        [致 MSDN 减速编译器用户的文章]

        【讨论】:

          【解决方案7】:

          Double 是一种内置类型。 FPU/SSE 核心(以前称为“数学协处理器”)支持它,这就是它速度极快的原因。尤其是在乘法和科学函数方面。

          十进制其实是一个复杂的结构,由若干个整数组成。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2019-11-27
            • 2012-11-07
            • 2010-10-22
            • 1970-01-01
            • 1970-01-01
            • 2016-10-01
            • 2020-05-28
            相关资源
            最近更新 更多