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可以得到最低的lo​​ng 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函数没有被选中,因为他传递的参数431都是int类型的,声明的函数具有 unsigned 类型的参数。从 C++11 开始,有很多 std::pow 的重载(或函数模板)。这些都是 return floatdouble(除非其中一个参数是 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 位,则结果完全按照标准定义。