【问题标题】:Errors multiplying large doubles乘以大双打的错误
【发布时间】:2014-08-05 03:26:40
【问题描述】:

我用 C++ 制作了一个使用双精度的 BOMDAS 计算器。每当我输入像

这样的表达式
1000000000000000000000*1000000000000000000000

我得到类似 1000000000000000000004341624882808674582528.000000 的结果。我怀疑它与浮点数有关。

【问题讨论】:

标签: c++ math floating-point double


【解决方案1】:

这不是错误。这正是因为浮点类型的表示方式,结果精确到double 精度。

计算机中的浮点类型以 (-1)sign * 尾数 * 2exp 的形式编写,因此它们只有 更广泛的范围,而不是无限的精度。它们仅精确到尾数精度,并且每次操作后的结果都将被四舍五入。 double 类型最常实现为带有 53 位尾数的 IEEE-754 64 位双精度,因此它可以正确地记录到 log(253) ≈ 15.955 个十进制数字。执行1e21*1e21 会产生1e42,当以双精度四舍五入到最接近的值时,会给出您看到的值。如果将其四舍五入到 16 位,则与 1e42 完全相同。

如果您需要更多范围,请使用doublelong double。如果您只使用整数,那么int64_t(或__int128 使用 gcc 和 64 位平台上的许多其他编译器)具有更高的精度(64/128 位与 53 位相比)。如果您需要更高的精度,请改用 arbitrary-precision arithmetic 库,例如 GMP

【讨论】:

  • 其实,“误差”是近似值与实际值之差的数学术语。 一个错误。
【解决方案2】:

double 存储 53 位精度。这大约是 15 位十进制数字。您的问题是 double 无法存储您尝试存储的位数。第 15 位小数后的数字将不准确。

【讨论】:

  • 所以如果我以某种方式将双精度的大小限制为 15 位,我会得到更准确的结果吗?
  • @SkyLightna 不,两个 15 位数字相乘得到一个 30 位数字。您只能正确获得结果的前 15 位(或更准确地说是 53 位)
  • @LưuVĩnhPhúc 所以我可以将结果转换为字符串并截断它以使其符合准确的限制,然后将其转换回双精度数?
  • 不,无论字符串可以存储多少精度,转换回双精度后它只能存储最多15位有效数字。就像我说的那样,如果适合您,请使用 int64_t __int128_t,否则您将需要使用任意精度算术。更不用说字符串效率极低,如果您使用字符串,库将使用基数 2^32(或 64 位系统中的 2^64)而不是基数 10
【解决方案3】:

浮点数表示具有固定大小表示的值。 double 可以以可以恢复十进制数字的形式表示 16 位十进制数字(在内部,它通常使用基数 2 存储值,这意味着它可以准确地表示大多数小数十进制值)。如果超过位数,该值将被适当地四舍五入。当然,结果是您不一定会得到您希望的数字:如果您显式或隐式地要求超过 16 个十进制数字(例如,通过将格式设置为 std::ios_base::fixed 并使用更大的数字比1e16) 格式会产生更多的数字:它将准确地表示内部保存的二进制值,我认为可能产生多达 54 个非零数字。

如果您想准确计算较大的值,您将需要一些可变大小的表示。由于您的值是整数,因此大整数表示可能有效。这些计算通常比 double 慢很多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-08
    • 2021-03-09
    • 1970-01-01
    相关资源
    最近更新 更多