简体   繁体   English

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

[英]How does this way of calculating large numbers work?

Typically, to handle integers that exceed the range of long long in C++, you'd have to represent them as string and perform operations on them that way. 通常,要处理超出C ++中long long范围的整数,您必须将它们表示为字符串并以这种方式对它们执行操作。 But, I found this code on the internet which seems to work like magic. 但是,我在互联网上发现这个代码似乎像魔术一样。 It calculates any sum of powers of two (without 2^0), even though it can't be stored in a 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();

}

How does it work? 它是如何工作的? Will it work for any operation involving large numbers? 它适用于涉及大量的任何操作吗? If the value of n is very big (I tried 1024) it just prints "inf". 如果n的值非常大(我试过1024)它只打印“inf”。 What's the upper-limit of the range of numbers that can be calculated this way? 可以这种方式计算的数字范围的上限是多少?

What exactly does the following part do and why does it do it? 以下部分到底做了什么以及它为什么这样做?

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

Will it work for any operation involving large numbers? 它适用于涉及大量的任何操作吗?

You can perform same operations with floating point as you can perform with integers. 您可以使用浮点执行与浮点相同的操作。 But each calculation involves an error, and not all integers are representable. 但每次计算都涉及错误,并非所有整数都可以表示。


What's the upper-limit of the range of numbers that can be calculated this way? 可以这种方式计算的数字范围的上限是多少?

Depends on the type of double precision floating point that your processor uses. 取决于处理器使用的双精度浮点类型。

You can find out the highest representable number with std::numeric_limits<double>::max() . 您可以使用std::numeric_limits<double>::max()找出最高可表示的数字。 However, the precision is very bad at these high numbers. 但是,这些高数字的精度非常差。 Not all integers can be represented up to this number. 并非所有整数都可以表示为此数字。 The maximum value of continuously representable integers is std::pow(std::numeric_limits<double>::radix, std::numeric_limits<double>::digits) . 可连续表示的整数的最大值是std::pow(std::numeric_limits<double>::radix, std::numeric_limits<double>::digits)


What exactly does the following part do and why does it do it? 以下部分到底做了什么以及它为什么这样做?

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

This can be simplified to 这可以简化为

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

It simply deducts 2 from the last (lowest) digit. 它只是从最后一个(最低)数字中扣除2。 It relies on the mathematical fact that a power of 2 is never 0 or 1 modulo 10 (except 2 0 ) in which case the last digit would become a non-digit character. 它依赖于数学事实,即2的幂不是0或1模10(除了2 0 ),在这种情况下,最后一个数字将变为非数字字符。

It also relies on the fact that pow(2, n + 1) - 2 == pow(2, n + 1) for n >= 54 . 它还依赖于pow(2, n + 1) - 2 == pow(2, n + 1) n >= 54的事实。 The code assumes that the floating point follows the ubiquitous binary IEEE-754 format in which std::pow(std::numeric_limits<double>::radix, std::numeric_limits<double>::digits) is std::pow(2, 54) . 代码假定浮点遵循无处不在的二进制IEEE-754格式,其中std::pow(std::numeric_limits<double>::radix, std::numeric_limits<double>::digits)std::pow(2, 54) When n is greater than or equal to 54, the result of calculation std::pow(2, 54 + 1) becomes so large, that if you deduct a small number such 2 from it, the closest representable result is the same that you started with. n大于或等于54时,计算结果std::pow(2, 54 + 1)变得如此之大,如果从中扣除一个小数字2,则最接近的可表示结果与您相同开始于。 The accuracy error of the calculation is equal to the smaller operand! 计算的准确度误差等于较小的操作数! That calculation simply cannot be performed with floating point numbers. 这个计算根本不能用浮点数来执行。 That is why it is fixed afterwards with digit character fiddling. 这就是为什么之后用数字字符摆弄它固定的原因。

All powers of 2 (up to the limit) are representable, so the power calculation itself never has any accuracy error. 2的所有幂(直到极限)都是可表示的,因此功率计算本身从不会出现任何精度误差。

You are looking at a rather ham-fisted implementation of a relatively simple trick. 你正在研究一个相对简单的技巧的火腿实现。

It is based on the fact that binary floating-point representation (eg IEEE 754 one) can represent 2 N precisely for rather large values of N (the range of the exponent part of the representation). 它基于以下事实:二进制浮点表示(例如,IEEE 754一)可以精确地表示2 N的相当大的N值(表示的指数部分的范围)。

This means that in a properly implemented standard library by doing this 这意味着通过这样做,在正确实现的标准库中

unsigned N = ...;

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

you can obtain the exact decimal representation of 2 N for such large values of N . 对于如此大的N值,您可以获得2 N的精确十进制表示。

Now if you take into account the fact that decimal representation of 2 N (N > 0) never ends in ...0 or an odd digit, you should understand that adding 1 or subtracting 1 or 2 from the resultant decimal representation can only modify its last digit (never produces a carry or borrow ). 现在,如果你考虑到2 N (N> 0)的十进制表示永远不会以...0或奇数结尾的事实,你应该理解,从结果十进制表示中加1或减1或2只能修改它的最后一位数字(从不产生进位借位 )。 This means that you can calculate 2 N +k for k = -2,-1,0,+1 by simply following the above with 这意味着你可以通过简单地按照上面的方法计算k = -2,-1,0,+ 1的2 N + k

s[s.size() - 1] += k;

If you additionally observe that a power of 2 cannot end in ...98 , you should realize that representations for k = +2,+3 can be obtained by 如果你另外观察到2的幂不能在...98结束,你应该意识到k = + 2,+ 3的表示可以通过

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

since any possible carry will never propagate more than 1 step. 因为任何可能的携带都不会传播超过1步。 (For brevity, I omitted the check for length ). (为简洁起见,我省略了检查长度)。

Similarly, since a power of 2 cannot end in ...02 , representations for k = -3,-4 can be obtained by 类似地,由于2的幂不能在...02结束,因此可以获得k = -3,-4的表示

if ((s[s.size() - 1] += k) < '0')
{
  s[s.size() - 1] += 10;
  --s[s.size() - 2];
}

In other words, in the original code there was no real need to subtract 2 early (in pow(2, n + 1) - 2 ). 换句话说,在原始代码中,没有必要提前减去2(在pow(2, n + 1) - 2 )。 And there was no real need to involve that 48 in last digit adjustment expression. 并且没有必要将48用于最后一个数字调整表达式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM