执行 64 位负整数运算时出现意外结果

Unexpected result doing 64-bit negative integer arithmetic

说明我的问题的最好方法是通过我实际代码中的这个片段:

ULONG uiDelaySec = 5;

ULONGLONG iiDaysOver = 0;
LONGLONG ii = -10000000LL * (LONGLONG)uiDelaySec / (2 + iiDaysOver);

//Why do I get 'ii' equal to 9223372036829775808?

PS。我是 运行 这个代码 Visual Studio 2008.

VS 中的调试器面板有问题。如果你 sprintf 你的变量是正确的 -25000000

非常奇怪的是,如果我如下更改代码,调试窗格是正确的。

int64_t ii = -10000000LL;
ii = ii * (int64_t)uiDelaySec;
ii /= (uint32_t)(2);

关键是如果最后一个除法:如果用 uint64_t 执行,调试视图有问题,用 uint32_t 不是...

问题好像出在-10000000LL * (long long)uiDelaySec除以(2 + iiDaysOver),因为后者是unsigned long long int,前者是unsigned value。我的 C-Fu 不足以解释为什么会发生这种情况,但下面一行给出了预期的结果,至少使用 gcc 是这样:

long long int ii = -10000000LL * (long long)uiDelaySec / (long long int)(2 + iiDaysOver);

让我们考虑一下这个说法

LONGLONG ii = -10000000LL * (LONGLONG)uiDelaySec / (2 + iiDaysOver);

一步步执行

首先执行的是子表达式

-10000000LL * (LONGLONG)uiDelaySec

类型为LONGLONG,值为

-50000000

然后这个结果必须除以 ULONGLONG 类型的操作数 (2 + iiDaysOver) 因为 iiDaysOver 被定义为

ULONGLONG iiDaysOver = 0;

要执行该操作,编译器应为其操作数使用通用类型。左操作数的类型为 LONGLONG,而右操作数的类型为 ULONGLONG。根据通常的算术转换规则,如果两种类型具有相同的等级,则将有符号类型转换为无符号类型。

来自 C 标准

6.3.1.1 布尔值、字符和整数

— The rank of any unsigned integer type shall equal the rank of the corresponding signed integer type, if any.

6.3.1.8 常用算术转换

Otherwise, both operands are converted to the unsigned integer type corresponding to the type of the operand with signed integer type.

因此左操作数将被转换为类型ULONGLONG。你会得到左操作数的负值

-50000000

解释为非负值将具有值

18446744073659551616

要确保它确实有效,您可以插入语句

printf( "%llu\n", ( unsigned long long )-50000000LL );

或者是否将您的类型定义用于基本类型

printf( "%llu\n", ( ULONGLONG )-50000000LL );

将此值除以 2 将得到无符号值

9223372036829775808?

这两种类型都可以表示为非负值:LONGLONGULONGLONG. 所以这个值将被分配给 ii 而无需任何转换。