通常,要处理超出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;
答案 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只能修改其最后一位(从不产生进位或借用)。这意味着你可以通过简单地按照上面的
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
结束,因此可以通过
if ((s[s.size() - 1] += k) < '0')
{
s[s.size() - 1] += 10;
--s[s.size() - 2];
}
换句话说,在原始代码中,没有必要提前减去2(在pow(2, n + 1) - 2
中)。并且没有必要将48
纳入最后的数字调整表达式。