什么 x 的值使得 x + 1 == x 和 x * 2 == x 都为真?

Whats a value of x such that both x + 1 == x and x * 2 == x is true?

标题基本上说明了一切。我的朋友给了我这个作为他在他正在做的一些 CTF 中解决的挑战。我想了一会儿,想不通。此外,INFINITY 不是有效答案。谢谢。

在 C 中发生这种情况的唯一方法是:

  • x 是无穷大(+∞ 或 −∞)(等效于 HUGE_VAL-HUGE_VAL 在不支持无穷大的实现中)。
  • x 是 NaN,C 实现对 NaN 使用非 IEEE-754 行为。
  • x在浮点表示中与无穷大相邻,使用了合适的有向舍入模式。
  • x+1x*2 溢出,C 标准未定义的结果行为恰好将比较报告为真。
  • x 未初始化,因此不确定,并且在 xx+1 == x 的两次出现中具有不同的值,因此比较为真,在 x*2 == x要么类似地取不同的值,要么取零值。
  • x 未初始化且具有自动存储持续时间且未获取其地址,因此该行为未由 C 标准定义,结果行为恰好将比较报告为真。

证明:

除了无穷大之外,这些陈述在数学上是实数错误的,因此这不可能由实数行为引起。因此,它一定是由某些非实数行为引起的,例如溢出、环绕、舍入、不确定值(在每次使用对象时可能不同)或未初始化的值。由于 * 被限制为具有算术操作数,因此我们只需要考虑算术类型。 (在 C++ 中,可以定义一个 class 来使比较为真。)

对于有符号整数,+* 具有完全定义值的非实数行为仅在存在溢出时才会发生,因此这是可能的。

对于无符号整数,具有完全定义值的非实数行为仅在 +* 存在回绕时发生。然后,使用环绕模 M,我们将有 x+1 = x+kM 对于某个整数 k,所以 1 = kM,这对于任何 M[ 都是不可能的=81=] 用于包装。

对于 _Bool 类型,对可能值的详尽测试消除了它们。

对于浮点数,+* 仅在舍入、下溢、溢出和 NaN 时才会出现具有完全定义值的非实数行为。 NaN 永远不会按照 IEEE-754 规则进行比较,因此它们不能满足这一点,除了 C 标准不需要符合 IEEE-754 的事实,因此实现可以选择使比较为真。

x*2 不会下溢,因为它增加了幅度。可以使 x+1 以指数范围小于精度的反常浮点格式下溢,但这不会产生 x+1 == xx+1 == x 可以通过舍入足够大的 x 来满足,但是 x*2 必须产生除 x.

以外的值

剩下溢出来了。如果 x 是最大的可表示有限数(因此是小于无穷大的最大可表示数),并且舍入模式是向下(向 −∞)或向零舍入,则 x+1 将产生 xx*2 将产生 x,因此比较结果为真。同样,可表示的最大负有限数将满足向上舍入(向 +∞)或向零舍入的比较。

等式将适用于 double x = HUGE_VAL;。从 C99 开始,引用 cppreference.com:

The HUGE_VALF, HUGE_VAL and HUGE_VALL macros expand to positive floating point constant expressions which compare equal to the values returned by floating-point functions and operators in case of overflow

示例代码:

#include <math.h>
#include <stdio.h>

int main() {
    double x = HUGE_VAL;
    printf("%d %d\n", x + 1 == x, 2 * x == x);
    return 0;
}

Output:

1 1

使用 C 预处理器求解 x

#include <stdio.h>

int main() {

#define x  1,1

    if (x + 1 == x)
        printf("x + 1 == x is true\n");

    if (x * 2 == x)
        printf("x * 2 == x is true\n");

    printf("x     = %d\n", x);
    printf("x + 1 = %d\n", x + 1);
    printf("x * 2 = %d\n", x * 2);

    return 0;
}

输出(省略警告:):

x + 1 == x is true
x * 2 == x is true
x     = 1
x + 1 = 1
x * 2 = 1