Visual Studio 2010 SP1 中的 64 位整数初始化错误



我有一个奇怪的错误/错误/自我愚蠢问题。我正在用 C 语言开发一个小应用程序,并且使用的是 Visual Studio 2010 SP1。违规代码:

uint64_t sum_squared_X = 65535*65535*64;
int64_t sum_squared_Y = 65535*65535*64;

调试时,我得到以下结果:

sum_squared_X = 18446744073701163072;
sum_squared_Y = -8388544;

问题是,为什么?uint64_t的最大值为 2^64-1 或 18446744073709551615,int64_t的最大值为 2^63-1 或 9223372036854775807。

65535*

65535*64 = 274869518400,低于两个最大值。那为什么我会得到这些结果呢?

我在这里完全迷路了,一些帮助将不胜感激。

简短回答:65535 乘以 65535,使用 32 大有符号算术,得到 -131,071。然后将其乘以 -64 并转换为 uint64_t(由于包装而产生更大的正值)或 int64_t(保留将 -131,071 乘以 64 的结果)。

长答案:

无后缀整数十进制常量的类型取决于其值。它是此列表中第一个可以表示其值的:int、long int、long long int。 (添加后缀或使用八进制或十六进制常量会更改列表。由于这些类型依赖于 C 实现,因此行为依赖于 C 实现。

在您的

机器中,int 很可能是 32 位。因此,"65535"的类型是int,"64"的类型也是int。

您的表达式以"65535

*65535"开头。这将 65,535 乘以 65,535。数学结果为 4,924,836,225(十六进制,0xfffe0001)。对于 32 位有符号 int,这会溢出可表示的值。这是 C 标准中未定义的行为。在许多实现中经常发生的情况是,值从 231-1(最高可表示值)"环绕"到 -231(最低可表示值)。相同行为的另一种观点是,数学结果的位0xfffe0001被解释为 32 位有符号 int 的编码。在 2 的补码中,0xffffe0001是 -131,071。

然后你的表达式乘以 64。 -131,071*64 不会溢出;结果是 -8,388,544(使用编码0xff800040)。

最后,使用结果初始化uint64_t或int64_t对象。此初始化会导致转换为目标类型。

int64_t转换很简单;转换的输入是 -8,388,544,这在int64_t中完全可以表示,因此结果是 -8,388,544,编译器可能只需扩展符号位(生成编码0xffffffffff800040)即可实现。

uint64_t转换很麻烦,因为 -8,388,544 无法在uint64_t中表示。根据 1999 C 标准 6.3.1.3 2,"通过反复添加或减去比新类型中可以表示的最大值多一个来转换值,直到该值在新类型的范围内。对于uint64_t,"比新类型中可以表示的最大值多 1"是 264。因此,结果为 -8,388,544 + 264,即 18,446,744,073,701,163,072。这也具有编码0xffffffffff800040。

对于从较窄宽度到较宽宽度的转换,这种添加大于最大值 1 的操作等效于将旧类型的符号位复制到新类型中的所有较高位(符号扩展)。对于从较宽宽度到较窄宽度的转换,相当于丢弃高位。在任何一种情况下,结果都是残余模 2n,其中 n 是新类型中的位数。

当我编译您的示例时,我清楚地收到其中每一行的整数常量溢出警告。这是因为右侧,即常量,通常存储在基本整数中。您必须更改这些值的存储以防止发生溢出情况。要解决此问题,请改为执行以下操作:

uint64_t sum_squared_X = (uint64_t)65535*65535*64;
int64_t sum_squared_Y = (uint64_t)65535*65535*64;

在此处阅读更多内容

相关内容

  • 没有找到相关文章

最新更新