这种计算大数的方式如何工作?

时间:2017-03-10 21:47:53

标签: c++ stringstream largenumber

通常,要处理超出C ++中long long范围的整数,您必须将它们表示为字符串并以这种方式对它们执行操作。但是,我在互联网上发现这个代码似乎像魔术一样。它计算任意两个幂的总和(没有2 ^ 0),即使它不能存储在很长的长期内。

#include <iostream>  
#include <cmath>  
#include <iomanip>  
#include <sstream>  
using namespace std;

int main() {
    int n;
    stringstream ss;
    cin >> n;

    ss << fixed << setprecision(0) << pow(2, n + 1) - 2;

    if (n >= 54) {
        string a = ss.str();

        a[a.size() - 1] = ((a[a.size() - 1] - 48) - 2) + 48;

        cout << a;
        return 0;
    }

    cout << ss.str();

}

它是如何工作的?它适用于涉及大量的任何操作吗?如果n的值非常大(我试过1024)它只打印“inf”。可以通过这种方式计算的数字范围的上限是多少?

以下部分究竟是做什么的,为什么会这样做?

a[a.size() - 1] = ((a[a.size() - 1] - 48) - 2) + 48;

2 个答案:

答案 0 :(得分:6)

  

它适用于任何涉及大数的操作吗?

您可以使用浮点执行与浮点相同的操作。但每次计算都涉及错误,并非所有整数都可以表示。

  

可以通过这种方式计算的数字范围的上限是多少?

取决于处理器使用的双精度浮点类型。

您可以使用std::numeric_limits<double>::max()找出代表性最高的数字。但是,这些高数字的精度非常差。并非所有整数都可以表示为此数字。可连续表示的整数的最大值为std::pow(std::numeric_limits<double>::radix, std::numeric_limits<double>::digits)

  

以下部分究竟是做什么的,为什么会这样做?

a[a.size() - 1] = ((a[a.size() - 1] - 48) - 2) + 48;

这可以简化为

a[a.size() - 1] -= 2;

它只是从最后一个(最低)数字中扣除2。它依赖于数学事实,即2的幂不是0或1模10(除了2 0 ),在这种情况下,最后一位将成为非数字字符。

它还依赖于pow(2, n + 1) - 2 == pow(2, n + 1) n >= 54的事实。该代码假定浮点遵循无处不在的二进制IEEE-754格式,其中std::pow(std::numeric_limits<double>::radix, std::numeric_limits<double>::digits)std::pow(2, 54)。当n大于或等于54时,计算结果std::pow(2, 54 + 1)变得如此之大,以至于如果从中扣除一个小数字2,则最接近的可表示结果与您开始时的结果相同。计算的准确度误差等于较小的操作数!这个计算根本不能用浮点数来执行。这就是之后用数字字符摆弄它的原因。

所有2的幂(达到极限)都是可表示的,因此功率计算本身从不会出现任何精度误差。

答案 1 :(得分:3)

你正在研究一个相对简单的技巧的火腿实施。

这是基于二进制浮点表示(例如IEEE 754 one)可以代表2 N 这一事实,正好代表N的相当大的值(指数部分的范围)代表)。

这意味着在正确实施的标准库中执行此操作

unsigned N = ...;

double d = std::pow(2.0, N);
std::stringstream str;
str << std::fixed << std::setprecision(0) << d;
std::string s = str.str();

对于如此大的N值,您可以获得2 N 的精确十进制表示。

现在,如果你考虑到2 N (N> 0)的十进制表示永远不会以...0或奇数结尾的事实,你应该理解添加1或从结果十进制表示中减去1或2只能修改其最后一位(从不产生进位借用)。这意味着你可以通过简单地按照上面的

来计算k = -2,-1,0,+ 1的2 N + k
s[s.size() - 1] += k;

如果你另外观察到2的幂不能在...98中结束,你应该意识到k = + 2,+ 3的表示可以通过

获得
if ((s[s.size() - 1] += k) > '9')
{
  s[s.size() - 1] -= 10;
  ++s[s.size() - 2];
}

因为任何可能的进位都不会传播超过1步。 (为简洁起见,我省略了检查长度)。

类似地,由于2的幂不能以...02结束,因此可以通过

获得k = -3,-4的表示。
if ((s[s.size() - 1] += k) < '0')
{
  s[s.size() - 1] += 10;
  --s[s.size() - 2];
}

换句话说,在原始代码中,没有必要提前减去2(在pow(2, n + 1) - 2中)。并且没有必要将48纳入最后的数字调整表达式。