提问人:emsa 提问时间:10/22/2023 最后编辑:chqrlieemsa 更新时间:10/25/2023 访问量:247
(x<y)==(-x>-y) 是真是假?
Is (x<y)==(-x>-y) true or false?
问:
问题出在cs:app3e 2.82。
我了解到,当 时,也是 ,但是x = INT_MIN
-x
-INT_MIN
#include <stdio.h>
#include <limits.h>
int main() {
int x = INT_MIN, y = -3;
printf("%d\n", (x < y) == (-x > -y));
return 0;
}
在我的机器上(Linux 版本 6.2.0-34-generic (buildd@bos03-amd64-059) (x86_64-linux-gnu-gcc-11 (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 2.38)),这给出了输出 1。 为什么会这样?
我使用 gcc -o 来编译它。 我也使用 gcc -O0 来编译它。
答:
在您的平台上,像许多其他平台一样(任何使用没有陷阱表示的二元补码的平台,这是最现代的平台),否定INT_MIN
是未定义的行为。编译器可以假设未定义的行为不会发生,并按照他们喜欢的方式行事,包括以荒谬的方式行事。因此,的优化器(无论优化设置如何,它仍然在最低级别运行)可以在分析时排除 的可能性,并得出结论,它是同义的,在不执行任何运行时比较的情况下进行替换。事实上,它不执行任何运行时比较,它只是直接加载以进行打印。gcc
INT_MIN
(x<y)==(-x>-y)
1
1
实际的常量,可能有点混乱。如果我们查看 C 标准 (C17 5.2.4.2.1),它说: 和 ,这意味着这些是 16 位必须支持的绝对最小值。为简单起见,下面的所有示例都假定为 16 位(32/64 位二的补码系统当然使用 2^31 - 1 和 -2^32 所以 /)。INT_MIN
INT_MAX
INT_MIN -32767
INT_MAX +32767
int
int
2147483647
-2147483648
为什么选择这些值是出于历史原因。除了行业标准 2 的补语外,C 还支持两种奇异的符号格式:one 的补语和有符号量级。在后两者中,我们有一个负零和/或陷阱表示,给出的可能值范围是 -32767 到 32767。
但是绝大多数计算机都使用二补码,然后在 32768 位系统上变为 -16。这符合标准,因为它只是说必须至少为 -32767。在二的补码中,仍然是 32767。INT_MIN
INT_MIN
INT_MAX
因此,在二的补码系统上,我们不能做,因为是 32767 并且不能保持值 32768。我们将创建一个整数溢出,这是未定义的行为 - 任何事情都可能发生,包括编译器生成奇怪和无意义的代码。int x = INT_MIN; x = -x;
INT_MAX
在即将到来的 C23 标准中,对异国情调签名格式的支持最终将从 C 中删除。然后也可能在标准中变成 -32768。INT_MIN
这给出了输出 1。为什么会这样?
正如其他答案已经解释的那样,有符号溢出是 C 语言中的未定义行为,因此编译器可以假设如果有符号,则不会溢出。-x
x
由于您的问题具体涉及:GCC 支持命令行选项 -fwrapv
,因此有符号溢出将类似于无符号溢出。添加到命令行选项(或 -fno-strict-overflow
)后,您的程序将打印 (false)。gcc
-fwrapv
0
此选项可能会禁用某些优化并启用其他优化。有趣的是,看到对略有不同的程序的影响
#include <stdio.h>
#include <limits.h>
int x = INT_MIN, y = -3;
int main()
{
printf ("%d\n", (x < y) == (-x > -y));
return 0;
}
其中 and 的值在编译时不再已知:x
y
当使用 编译时,编译器将替换为文本,而不管 和 的值如何。这是因为,如果没有发生算术溢出,则表达式的计算结果为 true,并且编译器可能会假定,如果溢出,则表达式不会溢出。这与有符号溢出的未定义行为一致。
-O2 -fno-wrapv
(x < y) == (-x > -y)
1
x
y
使用 编译时,编译器无法在编译时计算,它会生成代码以在运行时计算表达式。对于为 和 提供的特定值,结果为 false。
-O2 -fwrapv
(x < y) == (-x > -y)
x
y
您可能还查看了诊断选项,例如 -Wstrict-overflow
或 。当然.-Woverflow
-Wall
评论
-INT_MIN
是未定义的行为。请参见:stackoverflow.com/questions/71081310/...x
也是-INT_MIN
”并不完全正确,因为其他人描述的潜在 UB。但是,除了结果之外,您还期待什么?也许我需要咖啡......
1
(x < y) == (-x > -y)
-fwrapv
-fsanitize=undefined
-fwrapv
-x>-y