我正在尝试使用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;
}
我希望这两个代码是相同的,为什么不是这样?
答案 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
类型。