为什么我的int很长的算术表现为这种方式?

时间:2019-05-03 13:42:40

标签: c++ floating-point precision long-long

我正在尝试使用long long数据类型计算大整数,但是当它变得足够大(2^55)时,算术行为是不可预测的。我正在使用Microsoft Visual Studio 2017。

在第一种情况下,我在初始化时从2变量long long中减去了m。直到我尝试n为止,这对所有54都有效,然后m不会直接减去2

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <set>

using namespace std;

#define LL long long

int main()
{
    LL n;
    cin >> n;
    LL m = pow(2, n + 1) - 2;
    cout << m;
    return 0;
}

但是,使用此代码m确实会被2减去,并且按我的预期工作。

#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <set>

using namespace std;

#define LL long long

int main()
{
    LL n;
    cin >> n;
    LL m = pow(2, n + 1);
    m -= 2;
    cout << m;
    return 0;
}

我希望这两个代码是相同的,为什么不是这样?

3 个答案:

答案 0 :(得分:50)

问题

LL m = pow(2, n + 1) - 2;

pow(2, n + 1)不是long long。它的类型为double,并且由于值太大,因此从中减去2不会改变其值。这意味着m将没有正确的值。如您所知,您需要先分配结果,然后再进行减法。另一种选择是编写自己的pow,当给定整数类型时将返回整数类型,以便您可以同时进行幂和减。

答案 1 :(得分:19)

  

我希望这两个代码是相同的,为什么不是这样?

您的期望是错误的。您的第二个代码将与此等效:

if [[ -e "./doc/issues/$1.md" ]]; 

由于arithmetic operators的转换规则以及在这种情况下std::pow()返回auto m = static_cast<LL>( pow(2, n + 1) ) - 2; 的事实:

  

对于二进制运算符(移位除外),如果提升的操作数具有不同的类型,则将应用另外的一组隐式转换,这称为通常的算术转换,其目标是产生公共类型(也可以通过std :: common_type访问)类型特征)。如果在任何整数提升之前,一个操作数是枚举类型,而另一个操作数是浮点类型或其他枚举类型,则不建议使用此行为。 (自C ++ 20起)

     

如果任何一个操作数都具有范围枚举类型,则不执行任何转换:另一个操作数和返回类型必须具有相同的类型

     

否则,如果其中一个操作数为long double,则另一个操作数将转换为long double

     

否则,如果其中一个操作数为double,则另一个操作数将转换为double

     

否则,如果其中一个操作数为float,则另一个操作数将转换为float

     

...

(强调是我的),您的原始表达式将导致您回到double-double,而不是像第二种情况那样导致double-long long int,因此产生差异。 / p>

答案 2 :(得分:16)

pow函数返回类型为double的值,该值仅具有53位精度。尽管返回的值将适合double,即使n大于53,减2也会导致类型double的值需要超过53位的精度,因此减法四舍五入到最接近的可表示值。

之所以取消减法,是因为从double返回的pow值已分配给long long,然后从{{1 }}。

由于您不处理浮点数,而只将2提高到幂,因此您可以通过简单的向左移位来替换对int的调用:

long long

这会将所有中间值保留为pow类型。