long long value in Visual Studio

long long value in Visual Studio

本文关键字:long Studio Visual in value      更新时间:2023-10-16

我们知道 -2*4^31 + 1 = -9.223.372.036.854.775.807,这是您可以存储的最小值,如下所示:整数类型可以存储在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。

这是一个非常讨厌的小问题,有三个(!)原因。

首先,浮点运算是近似的。 如果编译器选择一个返回浮点数或双精度的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 的版本没有。

第三,OP 的pow函数没有被选中,因为他传递了参数431,它们都是类型int,并且声明的函数有类型unsigned的参数。 自 C++11 以来,std::pow有很多重载(或函数模板)。 这些都返回floatdouble(除非其中一个参数是类型long double- 这在这里不适用)。

因此,超载std::pow将是一个更好的匹配......使用双返回值,我们得到浮点舍入。

故事的寓意:不要编写与标准库函数同名的函数,除非你真的知道自己在做什么!

Visual Studio定义了pow(double, int),它只需要转换一个参数,而你的pow(unsigned, unsigned)需要转换两个参数,除非你使用pow(4U, 31U)。C++ 中的重载分辨率基于输入,而不是结果类型。

可以通过numeric_limits获得最低的多头长值。长期以来,它是:

auto lowest_ll = std::numeric_limits<long long>::lowest();

这导致:

-9223372036854775808

被调用的pow()函数不是你的,因此观察到的结果。更改函数的名称。

-9.223.372.036.854.775.808 结果的唯一可能解释是使用标准库中返回双精度值的pow函数。在这种情况下,5将低于双重计算的精度,结果将恰好是 -263并转换为长整型将给出0x8000000000000000-9.223.372.036.854.775.808

如果使用返回无符号长整型的函数,则会收到一条警告,指出您将一元减号应用于无符号类型,但仍会得到 ULL。因此,整个操作应作为无符号长长执行,并且应无溢出0x8000000000000005作为无符号值。当您将其转换为有符号值时,结果是未定义的,但是我认识的所有编译器都只是使用具有相同表示形式的有符号整数,即-9.223.372.036.854.775.803

但是,只需使用以下命令即可使计算长时间进行有符号

而没有任何警告:
long long nr =  -1 * pow(4, 31) + 5 - pow(4,31);

此外,这里既没有未定义的强制转换,也没有溢出,因此结果是按照标准完美定义的,前提是无符号长长度至少为 64 位。

你对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 位数字的 presicion! 要获得您要查找的答案,请将签名更改为:

long long my_pow(unsigned a, unsigned b);

这在MSVC++ 2013中对我有用。 如其他答案中所述,您获得浮点pow是因为您的函数期望unsigned,并接收有符号整数常量。 将U添加到整数中会调用您的pow版本。