为什么我的算术 long long int 会这样?
Why is my arithmetic with a long long int behaving this way?
我正在尝试使用 long long
数据类型计算大整数,但是当它变得足够大时 (2^55
),算术行为是不可预测的。我在 Microsoft Visual Studio 2017.
工作
在第一种情况下,我在初始化时从 long long
变量 m
中减去 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
(参考cppreference),因为这个值太大了,减去2
不会改变它的值。这意味着 m
不会有正确的值。正如您已经发现的那样,您需要先分配结果,然后再进行减法。另一种选择是编写自己的 pow
,当给定一个整数类型时,它将 return 一个整数类型,这样您就可以同时进行乘方和减法。
I expect both codes to be equivalent, why is this not the case?
你的期望是错误的。您的第二个代码将等效于此:
auto m = static_cast<LL>( pow(2, n + 1) ) - 2;
由于 arithmetic operators and the fact that std::pow()
returns double
的转换规则,在这种情况下:
For the binary operators (except shifts), if the promoted operands have different types, additional set of implicit conversions is applied, known as usual arithmetic conversions with the goal to produce the common type (also accessible via the std::common_type type trait). If, prior to any integral promotion, one operand is of enumeration type and the other operand is of a floating-point type or a different enumeration type, this behavior is deprecated. (since C++20)
If either operand has scoped enumeration type, no conversion is performed: the other operand and the return type must have the same type
Otherwise, if either operand is long double, the other operand is converted to long double
Otherwise, if either operand is double, the other operand is converted to double
Otherwise, if either operand is float, the other operand is converted to float
...
(重点是我的)你的原始表达式会导致 double
- double
而不是 long long int
- long long int
就像你在第二种情况下所做的那样,因此有所不同.
pow
函数 returns 一个 double
类型的值,它只有 53 位精度。虽然即使 n
大于 53,返回值也将适合 double
,减去 2 会导致需要超过 53 位精度的类型 double
的值,因此结果减法四舍五入到最接近的可表示值。
分解减法的原因是因为从 pow
返回的 double
值被分配给 long long
,然后从 int
中减去 int
=18=].
由于您不是在处理浮点数,而只是计算 2 的幂,因此您可以用简单的左移替换对 pow
的调用:
LL m = (1LL << (n + 1)) - 2;
这会将所有中间值保留为 long long
类型。
我正在尝试使用 long long
数据类型计算大整数,但是当它变得足够大时 (2^55
),算术行为是不可预测的。我在 Microsoft Visual Studio 2017.
在第一种情况下,我在初始化时从 long long
变量 m
中减去 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
(参考cppreference),因为这个值太大了,减去2
不会改变它的值。这意味着 m
不会有正确的值。正如您已经发现的那样,您需要先分配结果,然后再进行减法。另一种选择是编写自己的 pow
,当给定一个整数类型时,它将 return 一个整数类型,这样您就可以同时进行乘方和减法。
I expect both codes to be equivalent, why is this not the case?
你的期望是错误的。您的第二个代码将等效于此:
auto m = static_cast<LL>( pow(2, n + 1) ) - 2;
由于 arithmetic operators and the fact that std::pow()
returns double
的转换规则,在这种情况下:
For the binary operators (except shifts), if the promoted operands have different types, additional set of implicit conversions is applied, known as usual arithmetic conversions with the goal to produce the common type (also accessible via the std::common_type type trait). If, prior to any integral promotion, one operand is of enumeration type and the other operand is of a floating-point type or a different enumeration type, this behavior is deprecated. (since C++20)
If either operand has scoped enumeration type, no conversion is performed: the other operand and the return type must have the same type
Otherwise, if either operand is long double, the other operand is converted to long double
Otherwise, if either operand is double, the other operand is converted to double
Otherwise, if either operand is float, the other operand is converted to float
...
(重点是我的)你的原始表达式会导致 double
- double
而不是 long long int
- long long int
就像你在第二种情况下所做的那样,因此有所不同.
pow
函数 returns 一个 double
类型的值,它只有 53 位精度。虽然即使 n
大于 53,返回值也将适合 double
,减去 2 会导致需要超过 53 位精度的类型 double
的值,因此结果减法四舍五入到最接近的可表示值。
分解减法的原因是因为从 pow
返回的 double
值被分配给 long long
,然后从 int
中减去 int
=18=].
由于您不是在处理浮点数,而只是计算 2 的幂,因此您可以用简单的左移替换对 pow
的调用:
LL m = (1LL << (n + 1)) - 2;
这会将所有中间值保留为 long long
类型。