为什么我的算术与长 int 的行为是这样的?



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

在第一种情况下,我从初始化中mlong long变量中减去2。这适用于所有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;
}

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

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

pow(2, n + 1)不是long long. 它具有类型double(参考 cpp首选项),并且由于该值非常大,因此从中减去2不会更改其值。 这意味着m将没有正确的值。 正如您已经发现的,您需要先分配结果,然后再进行减法。 另一种选择是编写您自己的pow,当给定整数类型时,该将返回整数类型,以便您可以同时进行幂和减法的提高。

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

你的期望是错误的。您的第二个代码等效于以下内容:

auto m = static_cast<LL>( pow(2, n + 1) ) - 2;

由于算术运算符的转换规则以及在这种情况下std::pow()返回double的事实:

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

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

否则,如果其中一个操作数为长双精度,则另一个操作数将转换为长双精度

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

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

数。

(强调是我的)您的原始表达式将导致double-double而不是long long int-long long int就像您在第二种情况下所做的那样,因此有所不同。

pow函数返回一个类型为double的值,该值只有 53 位精度。 虽然即使n大于 53,返回值也适合double,但减去 2 会导致需要超过 53 位精度的double类型的值,因此减法的结果将舍入到最接近的可表示值。

分解减法工作的原因是,从pow返回的double值被分配给long long,然后你从long long中减去一个int

由于您不处理浮点数,并且仅将 2 提高到一个幂,因此您可以将调用替换为简单的左移pow

LL m = (1LL << (n + 1)) - 2;

这会将所有中间值保留在类型long long中。

相关内容

  • 没有找到相关文章

最新更新