【问题标题】:Loss of precision with pow function when surpassing 10^10 limit?超过 10^10 限制时 pow 函数的精度损失?
【发布时间】:2020-09-25 14:08:34
【问题描述】:

在做我的第一个 uni 作业,并遇到了这个问题:

任务:求所有 n 个元素的总和,其中 n 是数字中数字的个数(n=1,表示 1、2、3...例如 8、9,答案为 45)

问题:我写的代码已经正确地得到了所有测试答案,直到 10 的 9 次方,但是当它达到 10 的 10 次方时,答案开始出错,这真的很接近我应该得到,但不完全在那里(例如,我的输出 = 49499999995499995136,预期结果 = 49499999995500000000)

非常感谢一些帮助/见解,我猜这与变量类型有关,但不太确定可能的解决方案..

#include <iostream>
#include <cmath>
#include <iomanip>

using namespace std;

int main()
{
    int n;
    double ats = 0, maxi, mini;
    cin >> n;
    maxi = pow(10, n) - 1;
    mini = pow(10, n-1) - 1;
    ats = (maxi * (maxi + 1)) / 2 - (mini * (mini + 1)) / 2;
    cout << setprecision(0) << fixed << ats;
}

【问题讨论】:

  • pow 不能与 int 参数一起使用.. 寻找重复项
  • 如果 int n 10 就不能正常工作?如果我可以问的话,我应该用什么代替 int 呢?
  • @idclev463035818 这个问题实际上是由于 10^10 * 10^9 超过了double 的位。与pow 关系不大(我认为
  • 49,499,999,995,500,000,000 是一个天文数字 (10^20)。没有简单的方法来处理它而不损失标准 C++ 中的精度。请参阅here 以获得一些灵感。

标签: c++ c++11


【解决方案1】:

问题的主要原因是pow()函数。它适用于double,而不是int。失去准确性是代表巨大数字的代价。 解决问题的方法有3种:

  1. 对于小的 n,您可以创建自己的 long long int pow(int x, int pow) 函数。但是有一个问题,我们甚至可以溢出long long int
  2. 如@rustyx 所说,使用长算术函数。您可以使用矢量编写自己的,或者查找并包含库。
  3. 有专门针对主题任务的数学解决方案。它解决了大数字问题。 你可以写你的公式像 ((10^n) - 1) * (10^n) - (10^m - 1) * (10^m)) / 2 , (这里 m = n-1) 然后乘以分子中的数字。重新组合它们。提取公倍数 10^(n-1)。然后你可以看到,这个答案有一个结构: X9...9Y0...0 表示足够大的 n,其中字母 X 和 Y 是常数。 因此,您可以直接打印答案“字符串”而无需计算。

【讨论】:

  • 也谢谢你的第三点!,这实际上也是我们的策展人解决这个问题的方式,现在他们与我们分享了它,只是把答案写下来,而不是将它存储为变量:)
【解决方案2】:

我认为您将浮点数拉伸到超出其精度的范围。让我解释一下:

C pow() 函数将双精度数作为参数。您正在传递整数,编译器正在添加代码以在它们到达 pow() 之前将它们转换为双精度数。 (无论如何,当您以这种方式声明返回值时,您将其存储为双精度值)。

浮点数之所以这样称呼,正是因为点“浮动”。在 double 中,有一个符号位,一些位用于尾数,一些位用于指数。在二进制中,提升到 2 的幂相当于将小数点向右移动(如果提升到负数,则向左移动)。所以基本上指数是用二进制表示小数点在哪里。对双精度数使用这种内存中表示的最大优势是,对于接近 0 的数字,您可以获得很多精度,而随着数字变大,精度会逐渐降低。

最后一件事就是发生在你身上的事。您的号码太大而无法准确存储。所以它被四舍五入到最接近的 2 的幂和(2 的幂是二进制中右侧全为零的数字)。

快速实验:在浏览器中按 F12,打开 javascript 控制台并输入 49499999995499995136。就我而言,在 chrome 中,我重现了同样的问题。

如果你真的真的很想用这么大的数字精确,那么你可以试试some of these libraries,但这对于学生计划来说太高级了,你不需要它。如果用户输入的数字太大,只需添加一个 if 块并打印错误消息(教授喜欢,这实际上是非常正确的)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-05-31
    • 1970-01-01
    • 2013-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-19
    • 1970-01-01
    相关资源
    最近更新 更多