Visual Studio 中的 long long 值
long long value in Visual Studio
我们知道 -2*4^31 + 1 = -9.223.372.036.854.775.807,你可以在 long long 中存储的最低值,如这里所说: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.
通过numeric_limits可以得到最低的long long值。很长一段时间是:
auto lowest_ll = std::numeric_limits<long long>::lowest();
这导致:
-9223372036854775808
被调用的 pow()
函数不是您的,因此观察到的结果。更改函数的名称。
Visual Studio 定义了 pow(double, int)
,它只需要转换一个参数,而你的 pow(unsigned, unsigned)
需要转换两个参数,除非你使用 pow(4U, 31U)
。 C++ 中的重载分辨率基于输入 - 而不是结果类型。
您对 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;
}
此代码报错:"unary minus operator applied to unsigned type, result still unsigned"。因此,从本质上讲,您的原始代码调用了一个浮点函数,取反了该值,对其应用了一些整数运算,但它没有足够的精度来给出您正在寻找的答案(19 位精度!)。要得到你要找的答案,请将签名更改为:
long long my_pow(unsigned a, unsigned b);
这在 MSVC++ 2013 中对我有用。如其他答案中所述,您将获得浮点数 pow
,因为您的函数需要 unsigned
,并接收有符号整数常量。将 U
添加到您的整数会调用您的 pow
.
版本
这是一个非常讨厌的小问题,有三个(!)原因。
首先有个问题就是浮点运算是近似的。如果编译器选择 pow
函数 returning float 或 double,那么 4**31 太大以至于 5 小于 1ULP(最小精度单位),所以添加它不会有任何作用(在换句话说,4.0**31+5 == 4.0**31)。乘以-2可以无损,结果可以无损存入一个long long
作为错误答案:-9.223.372.036.854.775.808.
其次,标准 header 可以 包含其他标准 header,但这不是必需的。显然,Visual Studio 的 <iostream>
版本包括 <math.h>
(在全局命名空间中声明 pow
),但 Code::Blocks 的版本没有。
第三,OP的pow
函数没有被选中,因为他传递的参数4
和31
都是int
类型的,声明的函数具有 unsigned
类型的参数。从 C++11 开始,有很多 std::pow
的重载(或函数模板)。这些都是 return float
或 double
(除非其中一个参数是 long double
类型——此处不适用)。
因此 std::pow
的重载将更好地匹配......具有双 return 值,并且我们得到浮点舍入。
故事的寓意:不要编写与标准库函数同名的函数,除非你真的知道你在做什么!
-9.223.372.036.854.775.808 结果的唯一可能解释是使用标准库中的 pow
函数返回双精度值。在那种情况下,5
将低于 double 计算的精度,结果将恰好是 -263 并转换为 long long 将得到 0x8000000000000000
或 -9.223.372.036.854.775.808
.
如果您使用返回 unsigned long long 的函数,您会收到一条警告,指出您将一元负号应用于无符号类型,但仍然会得到一个 ULL。所以整个操作应该作为 unsigned long long 执行,并且应该在没有溢出的情况下给出 0x8000000000000005
作为无符号值。当您将它转换为带符号的值时,结果是未定义的,但我知道的所有编译器都只使用具有相同表示形式的带符号整数,即 -9.223.372.036.854.775.803
.
但是只要使用:
,就可以很简单地在没有任何警告的情况下使计算为 signed long long
long long nr = -1 * pow(4, 31) + 5 - pow(4,31);
此外,这里既没有未定义的强制转换也没有溢出,因此如果 unsigned long long 至少为 64 位,则结果完全按照标准定义。
我们知道 -2*4^31 + 1 = -9.223.372.036.854.775.807,你可以在 long long 中存储的最低值,如这里所说: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.
通过numeric_limits可以得到最低的long long值。很长一段时间是:
auto lowest_ll = std::numeric_limits<long long>::lowest();
这导致:
-9223372036854775808
被调用的 pow()
函数不是您的,因此观察到的结果。更改函数的名称。
Visual Studio 定义了 pow(double, int)
,它只需要转换一个参数,而你的 pow(unsigned, unsigned)
需要转换两个参数,除非你使用 pow(4U, 31U)
。 C++ 中的重载分辨率基于输入 - 而不是结果类型。
您对 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;
}
此代码报错:"unary minus operator applied to unsigned type, result still unsigned"。因此,从本质上讲,您的原始代码调用了一个浮点函数,取反了该值,对其应用了一些整数运算,但它没有足够的精度来给出您正在寻找的答案(19 位精度!)。要得到你要找的答案,请将签名更改为:
long long my_pow(unsigned a, unsigned b);
这在 MSVC++ 2013 中对我有用。如其他答案中所述,您将获得浮点数 pow
,因为您的函数需要 unsigned
,并接收有符号整数常量。将 U
添加到您的整数会调用您的 pow
.
这是一个非常讨厌的小问题,有三个(!)原因。
首先有个问题就是浮点运算是近似的。如果编译器选择 pow
函数 returning float 或 double,那么 4**31 太大以至于 5 小于 1ULP(最小精度单位),所以添加它不会有任何作用(在换句话说,4.0**31+5 == 4.0**31)。乘以-2可以无损,结果可以无损存入一个long long
作为错误答案:-9.223.372.036.854.775.808.
其次,标准 header 可以 包含其他标准 header,但这不是必需的。显然,Visual Studio 的 <iostream>
版本包括 <math.h>
(在全局命名空间中声明 pow
),但 Code::Blocks 的版本没有。
第三,OP的pow
函数没有被选中,因为他传递的参数4
和31
都是int
类型的,声明的函数具有 unsigned
类型的参数。从 C++11 开始,有很多 std::pow
的重载(或函数模板)。这些都是 return float
或 double
(除非其中一个参数是 long double
类型——此处不适用)。
因此 std::pow
的重载将更好地匹配......具有双 return 值,并且我们得到浮点舍入。
故事的寓意:不要编写与标准库函数同名的函数,除非你真的知道你在做什么!
-9.223.372.036.854.775.808 结果的唯一可能解释是使用标准库中的 pow
函数返回双精度值。在那种情况下,5
将低于 double 计算的精度,结果将恰好是 -263 并转换为 long long 将得到 0x8000000000000000
或 -9.223.372.036.854.775.808
.
如果您使用返回 unsigned long long 的函数,您会收到一条警告,指出您将一元负号应用于无符号类型,但仍然会得到一个 ULL。所以整个操作应该作为 unsigned long long 执行,并且应该在没有溢出的情况下给出 0x8000000000000005
作为无符号值。当您将它转换为带符号的值时,结果是未定义的,但我知道的所有编译器都只使用具有相同表示形式的带符号整数,即 -9.223.372.036.854.775.803
.
但是只要使用:
,就可以很简单地在没有任何警告的情况下使计算为 signed long longlong long nr = -1 * pow(4, 31) + 5 - pow(4,31);
此外,这里既没有未定义的强制转换也没有溢出,因此如果 unsigned long long 至少为 64 位,则结果完全按照标准定义。