什么 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+1
和 x*2
溢出,C 标准未定义的结果行为恰好将比较报告为真。
x
未初始化,因此不确定,并且在 x
在 x+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 == x
。 x+1 == x
可以通过舍入足够大的 x
来满足,但是 x*2
必须产生除 x
.
以外的值
剩下溢出来了。如果 x
是最大的可表示有限数(因此是小于无穷大的最大可表示数),并且舍入模式是向下(向 −∞)或向零舍入,则 x+1
将产生 x
和 x*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;
}
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
标题基本上说明了一切。我的朋友给了我这个作为他在他正在做的一些 CTF 中解决的挑战。我想了一会儿,想不通。此外,INFINITY 不是有效答案。谢谢。
在 C 中发生这种情况的唯一方法是:
x
是无穷大(+∞ 或 −∞)(等效于HUGE_VAL
或-HUGE_VAL
在不支持无穷大的实现中)。x
是 NaN,C 实现对 NaN 使用非 IEEE-754 行为。x
在浮点表示中与无穷大相邻,使用了合适的有向舍入模式。x+1
和x*2
溢出,C 标准未定义的结果行为恰好将比较报告为真。x
未初始化,因此不确定,并且在x
在x+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 == x
。 x+1 == x
可以通过舍入足够大的 x
来满足,但是 x*2
必须产生除 x
.
剩下溢出来了。如果 x
是最大的可表示有限数(因此是小于无穷大的最大可表示数),并且舍入模式是向下(向 −∞)或向零舍入,则 x+1
将产生 x
和 x*2
将产生 x
,因此比较结果为真。同样,可表示的最大负有限数将满足向上舍入(向 +∞)或向零舍入的比较。
等式将适用于 double x = HUGE_VAL;
。从 C99 开始,引用 cppreference.com:
The
HUGE_VALF
,HUGE_VAL
andHUGE_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;
}
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