我们知道-2 * 4 ^ 31 + 1 = -9.223.372.036.854.775.807,这是你可以存储的最长值,如下所述:What range of values can integer types store in C++。 所以我有这个操作:
#include <iostream>
unsigned long long pow(unsigned a, unsigned b) {
unsigned long long p = 1;
for (unsigned i = 0; i < b; i++)
p *= a;
return p;
}
int main()
{
long long nr = -pow(4, 31) + 5 -pow(4,31);
std::cout << nr << std::endl;
}
为什么显示-9.223.372.036.854.775.808而不是-9.223.372.036.854.775.803?我正在使用Visual Studio 2015。
答案 0 :(得分:14)
这是一个非常讨厌的小问题,有三个(!)原因。
首先,存在浮点运算是近似的问题。如果编译器选择返回float或double的pow
函数,那么4 ** 31是如此之大,以至于5小于1ULP(精度最低的单位),因此添加它将无效(换句话说,4.0 * * 31 + 5 == 4.0 ** 31)。乘以-2可以毫无损失地完成,并且结果可以存储在long long
中而不会丢失作为错误的答案:-9.223.372.036.854.775.808。
其次,标准标题可能包含其他标准标题,但不是必需的。显然,Visual Studio的<iostream>
版本包括<math.h>
(在全局命名空间中声明pow
),但Code :: Blocks'版本不包括。{/ p>
第三,未选择OP的pow
函数,因为他传递的参数4
和31
都是int
类型,并且声明的函数有参数类型为unsigned
。从C ++ 11开始,std::pow
有很多重载(或函数模板)。这些都返回float
或double
(除非其中一个参数的类型为long double
- 这里不适用)。
因此std::pow
的重载将更好地匹配...具有双返回值,并且我们得到浮点舍入。
故事的道德:不要编写与标准库函数同名的函数,除非你真的知道你在做什么!
答案 1 :(得分:3)
Visual Studio定义了pow(double, int)
,只需要转换一个参数,而pow(unsigned, unsigned)
需要转换两个参数,除非您使用pow(4U, 31U)
。 C ++中的重载分辨率基于输入 - 而不是结果类型。
答案 2 :(得分:2)
可以通过numeric_limits获得最低的长期长值。很长一段时间,它是:
auto lowest_ll = std::numeric_limits<long long>::lowest();
导致:
-9223372036854775808
被调用的pow()
函数不是您的,因此观察到的结果。更改功能的名称。
答案 3 :(得分:2)
对-9.223.372.036.854.775.808结果唯一可能的解释是使用标准库中的pow
函数返回double值。在这种情况下,5
将低于双精度计算的精度,结果将精确为-2 63 并转换为long long将给出0x8000000000000000
或-9.223.372.036.854.775.808
。
如果您使用函数返回unsigned long long,则会收到一条警告,提示您将unary减号应用于无符号类型并仍然获得ULL。因此整个操作应该以unsigned long long的形式执行,并且应该在没有溢出0x8000000000000005
的情况下作为无符号值。当您将其转换为有符号值时,结果是未定义的,但我知道的所有编译器只使用带有相同表示形式的有符号整数-9.223.372.036.854.775.803
。
但只要使用:
就可以很简单地将计算设置为长签名而不会发出任何警告long long nr = -1 * pow(4, 31) + 5 - pow(4,31);
另外,你既没有未定义的强制转换也没有溢出,因此如果无符号long long至少为64位,则每个标准都可以完美地定义结果。
答案 4 :(得分:1)
您对pow
的第一次调用是使用C标准库的函数,该函数在浮点上运行。尝试为pow
函数指定一个唯一名称:
unsigned long long my_pow(unsigned a, unsigned b) {
unsigned long long p = 1;
for (unsigned i = 0; i < b; i++)
p *= a;
return p;
}
int main()
{
long long nr = -my_pow(4, 31) + 5 - my_pow(4, 31);
std::cout << nr << std::endl;
}
此代码报告错误:“一元减运算符应用于无符号类型,结果仍未签名”。所以,基本上,你的原始代码称为浮点函数,否定了该值,对它应用了一些整数运算,因为它没有足够的精度来给出你想要的答案(在19位数的预测!)。要获得您正在寻找的答案,请将签名更改为:
long long my_pow(unsigned a, unsigned b);
这在MSVC ++ 2013中对我有用。正如其他答案所述,你得到了浮点pow
,因为你的函数需要unsigned
,并且接收有符号整数常量。将U
添加到整数会调用您的pow
版本。