执行 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?
这两种类型都可以表示为非负值:LONGLONG
和 ULONGLONG.
所以这个值将被分配给 ii
而无需任何转换。
说明我的问题的最好方法是通过我实际代码中的这个片段:
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?
这两种类型都可以表示为非负值:LONGLONG
和 ULONGLONG.
所以这个值将被分配给 ii
而无需任何转换。