(x<y)==(-x>-y) 是真是假?

Is (x<y)==(-x>-y) true or false?

提问人:emsa 提问时间:10/22/2023 最后编辑:chqrlieemsa 更新时间:10/25/2023 访问量:247

问:

问题出在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 来编译它。

C GCC 系统

评论

10赞 Paul Hankin 10/22/2023
-INT_MIN是未定义的行为。请参见:stackoverflow.com/questions/71081310/...
1赞 ad absurdum 10/22/2023
这个问题我不清楚。“我了解到,当 x = INT_MIN 时,-x 也是 -INT_MIN并不完全正确,因为其他人描述的潜在 UB。但是,除了结果之外,您还期待什么?也许我需要咖啡......1(x < y) == (-x > -y)
1赞 chux - Reinstate Monica 10/22/2023
“我了解到,当 x = INT_MIN 时,-x 也是 -INT_MIN”——>错误的结论。
2赞 Marc Glisse 10/22/2023
你可以尝试得到你所期望的行为,或者得到一个解释。-fwrapv-fsanitize=undefined
0赞 Peter Cordes 10/29/2023
相关:找到一个值 y 这样的值 (x < y) == (-x > -y) 将是假的,当 x 是有符号整数且 x=1 时? (如果没有明确定义的整数环绕,例如从 )。半相关:GCC 针对 x86-64 和 AArch64 自行进行了不同的优化:C 程序“(-x > -y)”的输出在 macOS 和 Linux 上有所不同 - 为什么以及如何修复它-fwrapv-x>-y

答:

14赞 ShadowRanger 10/22/2023 #1

在您的平台上,像许多其他平台一样(任何使用没有陷阱表示的二元补码的平台,这是最现代的平台),否定INT_MIN是未定义的行为。编译器可以假设未定义的行为不会发生,并按照他们喜欢的方式行事,包括以荒谬的方式行事。因此,的优化器(无论优化设置如何,它仍然在最低级别运行)可以在分析时排除 的可能性,并得出结论,它是同义的,在不执行任何运行时比较的情况下进行替换。事实上,它不执行任何运行时比较,它只是直接加载以进行打印。gccINT_MIN(x<y)==(-x>-y)11

2赞 Lundin 10/23/2023 #2

实际的常量,可能有点混乱。如果我们查看 C 标准 (C17 5.2.4.2.1),它说: 和 ,这意味着这些是 16 位必须支持的绝对最小值。为简单起见,下面的所有示例都假定为 16 位(32/64 位二的补码系统当然使用 2^31 - 1 和 -2^32 所以 /)。INT_MININT_MAXINT_MIN -32767INT_MAX +32767intint2147483647-2147483648

为什么选择这些值是出于历史原因。除了行业标准 2 的补语外,C 还支持两种奇异的符号格式:one 的补语有符号量级。在后两者中,我们有一个负零和/或陷阱表示,给出的可能值范围是 -32767 到 32767。

但是绝大多数计算机都使用二补码,然后在 32768 位系统上变为 -16。这符合标准,因为它只是说必须至少为 -32767。在二的补码中,仍然是 32767。INT_MININT_MININT_MAX

因此,在二的补码系统上,我们不能做,因为是 32767 并且不能保持值 32768。我们将创建一个整数溢出,这是未定义的行为 - 任何事情都可能发生,包括编译器生成奇怪和无意义的代码。int x = INT_MIN; x = -x;INT_MAX

在即将到来的 C23 标准中,对异国情调签名格式的支持最终将从 C 中删除。然后也可能在标准中变成 -32768。INT_MIN

0赞 emacs drives me nuts 10/25/2023 #3

这给出了输出 1。为什么会这样?

正如其他答案已经解释的那样,有符号溢出是 C 语言中的未定义行为,因此编译器可以假设如果有符号,则不会溢出。-xx

由于您的问题具体涉及:GCC 支持命令行选项 -fwrapv,因此有符号溢出将类似于无符号溢出。添加到命令行选项(或 -fno-strict-overflow)后,您的程序将打印 (false)。gcc-fwrapv0

此选项可能会禁用某些优化并启用其他优化。有趣的是,看到对略有不同的程序的影响

#include <stdio.h>
#include <limits.h>

int x = INT_MIN, y = -3;

int main()
{
    printf ("%d\n", (x < y) == (-x > -y));
    return 0;
}

其中 and 的值在编译时不再已知:xy

  • 当使用 编译时,编译器将替换为文本,而不管 和 的值如何。这是因为,如果没有发生算术溢出,则表达式的计算结果为 true,并且编译器可能会假定如果溢出,则表达式不会溢出。这与有符号溢出的未定义行为一致。-O2 -fno-wrapv(x < y) == (-x > -y)1xy

  • 使用 编译时,编译器无法在编译时计算,它会生成代码以在运行时计算表达式。对于为 和 提供的特定值,结果为 false。-O2 -fwrapv(x < y) == (-x > -y)xy

您可能还查看了诊断选项,例如 -Wstrict-overflow 或 。当然.-Woverflow-Wall