Edv*_*n K 31 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;
}
Run Code Online (Sandbox Code Playgroud)
但是,使用此代码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;
}
Run Code Online (Sandbox Code Playgroud)
我希望这两个代码是等效的,为什么不是这样?
Nat*_*ica 50
与问题
LL m = pow(2, n + 1) - 2;
Run Code Online (Sandbox Code Playgroud)
那pow(2, n + 1)
不是一个long long
。它具有类型double
(请参阅cppreference),并且由于值太大,因此2
从中减去不会更改其值。这意味着m
将没有正确的值。如您所知,您需要先分配结果,然后再进行减法。另一种选择是编写自己的pow
函数,当给定整数类型时将返回整数类型,以便您可以同时进行幂和减法运算。
Sla*_*ica 19
我希望这两个代码是等效的,为什么不是这样?
您的期望是错误的。您的第二个代码将等效于此:
auto m = static_cast<LL>( pow(2, n + 1) ) - 2;
Run Code Online (Sandbox Code Playgroud)
由于算术运算符的转换规则以及在这种情况下std::pow()
返回的事实double
:
对于二进制运算符(移位除外),如果提升的操作数具有不同的类型,则将应用其他隐式转换集(称为通常的算术转换),以产生通用类型(也可以通过std :: common_type类型特征访问) 。如果在任何整数提升之前,一个操作数是枚举类型,而另一个操作数是浮点类型或其他枚举类型,则不建议使用此行为。(自C ++ 20起)
如果一个操作数具有范围枚举类型,则不执行任何转换:另一个操作数和返回类型必须具有相同的类型
否则,如果其中一个操作数为long double,则另一个操作数将转换为long double
否则,如果其中一个操作数为double,则另一个操作数将转换为double
否则,如果其中一个操作数为float,则另一个操作数将转换为float
...
(强调是我的)您的原始表达会导致double
- double
而不是long long int
- long long int
与第二种情况一样,因此会产生差异。
dbu*_*ush 16
该pow
函数返回type的值double
,该值仅具有53位精度。尽管返回的值double
即使n
大于53 也适合,即使减去53 也会导致类型的值double
需要超过53位的精度,因此减法的结果将四舍五入为最接近的可表示值。
进行减法运算的原因是因为从double
返回的值pow
分配给a long long
,然后int
从a中减去a long long
。
由于您不处理浮点数,而只将2提高至幂,因此您可以pow
使用简单的左移代替到的电话:
LL m = (1LL << (n + 1)) - 2;
Run Code Online (Sandbox Code Playgroud)
这样会将所有中间值保持为type long long
。
归档时间: |
|
查看次数: |
2741 次 |
最近记录: |