【问题标题】:How to prove that the C statement -x, ~x+1, and ~(x-1) yield the same results?如何证明 C 语句 -x、~x+1 和 ~(x-1) 产生相同的结果?
【发布时间】:2010-02-17 05:24:36
【问题描述】:

我想知道这句话背后的逻辑,证明。 C 表达式 -x、~x+1 和 ~(x-1) 对于任何 x 都产生相同的结果。对于具体的例子,我可以证明这是正确的。我认为证明这一点的方法与二进制补码的性质有关。有任何想法吗?

【问题讨论】:

  • 当然,你总是可以对每个可表示的整数都这样做。这是一个证明,但不是一个非常有趣或有启发性的证明。
  • 恭维,三人组bada bing

标签: c proof twos-complement


【解决方案1】:

考虑将数字添加到其按位补码时会得到什么。 n 位整数 x 的按位补码在 x 为 0 的任何地方都为 1,反之亦然。所以很明显:

x + ~x = 0b11...11(全为 n 位)

不管 x 的位数是多少。此外,请注意,将一个填充为全 1 的 n 位数字加 1 将使其回绕为零。因此我们看到:

x + ~x + 1 = 0b11...11 + 1 = 0 和 ~x + 1 = -x。

同样,注意 (x - 1) + ~(x - 1) = 0b11...11。然后 (x - 1) + ~(x - 1) + 1 = 0,并且 ~(x - 1) = -x。

【讨论】:

  • 一个非常优雅的证明。我喜欢!
  • 这个证明适用于无符号整数。对于 C 语言,您无法证明有符号整数的结果,因为它不一定正确。
【解决方案2】:

我不确定您是否可以通过任何有用的公理来证明这一点,除了相当琐碎的归约,回到我们在现代整数 ALU 中将负数定义为二进制补码这一事实。

计算机必须使用二进制补码硬件来实现,只是有各种吸引人的特性,而且现在几乎所有东西都是这样构建的。 (但不是浮点数!那些是补码!)

因此,我们构建了一台机器,它恰好在 2 的补码中表示负数。显示用二进制补码表示的负数的表达式是准确的,但这仅仅是因为我们以这种方式定义了它们。这是现代机器中负整数的公理基础。

由于我们根据二进制补码来定义否定,因此您本质上指的是公理,尽管我认为这就是所有证明最终要做的事情。

也许这就是为什么我不是一个真正的理论家。 :-)

【讨论】:

  • 我投票赞成这个。 C 标准中根本没有要求使用 2 的补码。这只是 一种 允许的编码方案。
  • 这是一个很好的观点。可以对问题进行编辑以明确这一点。
  • @paxdiablo:如果整数类型是无符号的,二进制补码是无关紧要的,C语言可以证明。
  • @R,我认为这不对。如果实现使用反码,则-x~x 完全相同。如果您随后将一个添加到后者 (~x + 1),则它们 相等。例如,3 的八位 1 是 0000-0011~31111-1100~3 + 11111-1101,即 -2
【解决方案3】:

~x+1 等价于 -x 的 2 的补码 + 1(即负数)表示,~(x-1) 也表示相同(考虑最后一位为 1 的情况,~(x-1) = ~(b1b2.b(n-1)1 - 0) = b1'b2'...b(n-1)'1 = b1'b2'...b(n-1)'0 + 1 = ~x+1. 最后一位的类似情况保持为 0。 ~(x-1) = ~(b1b2..bi100..00 - 1) = ~b1b2..bi011..11 = b1'b2'..bi '100..00 = b1'b2'..bi'011..11 + 1 = ~x + 1.

【讨论】:

  • 我看不出以任何方式引用二进制补码的有点武断的定义如何代表二进制补码恒等式的代数陈述的证明。这不只是定义吗?这就像“证明”#ff0000 是红色的,因为它是 RGB 颜色值...
  • 我看不出粗鲁有什么帮助,只是投反对票并继续前进?
  • 虽然这篇文章并不完全清楚,主要是由于格式问题,但它是完全正确的,而且我认为 DigitalRoss 的投诉是无效的。
  • 我并没有故意粗鲁,也没有投反对票。顺便说一句,相当准确地暗示,由于我们只是定义取反的数字以2的补码表示,因此我们无法证明它有什么问题..因为它是一个公理。一种是根据公理证明陈述。我想最终证明可以是任何可以简化为公理的东西,但这似乎很接近于简单地重申一个。
  • 提出观点或提出严肃问题并不无礼。
【解决方案4】:

我将尝试提供一个每个人都应该觉得方便的直观解释。如果您坚持,我们可能会尝试更正式的方法。

在二进制补码表示中,为了使零元素具有唯一表示,我们牺牲了一个正元素。结果,多了一个没有正镜像的负数。

因此,给定 2 位,我们得到:{+1, 0, -1, -2},在二进制中表示为:

-2    10
-1    11
 0    00
+1    01

因此,我们可以将零视为一面镜子。现在,给定一个整数 x,如果我们想反转它的符号,我们可以从反转所有位开始。如果正面和负面之间没有零,这就足够了。但由于零会发生转变,因此我们已经对此进行了补偿。

问题中提到的两个表达式在~(x-1) 之前和~x+1 反转位之后进行此补偿。您可以在我们的 2 位示例中使用 +1-1 轻松验证这一点。

【讨论】:

    【解决方案5】:

    通常情况并非如此,因为 C 标准不要求使用二进制补码来表示负数。

    特别是,没有定义将 ~ 应用于有符号类型的结果。

    然而,据我所知,所有现代机器都对整数使用二进制补码。

    【讨论】:

    • +1 我同意“所有现代机器都使用...”部分,但您必须注意,编译器也可能会优化某些签名表达式,假设它不会溢出,因为如果表达式确实如此,则行为将是未定义的,因此编译器不必关心。 lists.gforge.inria.fr/pipermail/frama-c-discuss/2009-June/…中有一个例子
    • 所有现代机器可能都使用二进制补码,但某些 DSP(如 Motorola 56xxx)也允许饱和算法,因此不会出现上溢/下溢。 0x7fffff + 1 == 0x7fffff,0x800000 - 1 == 0x800000。
    • @Pascal Cuoq 这太了不起了。我看到它发生在带有 -O2 或更高版本的 gcc 4.3.2 中。我还将 Frama-C 列入了我的观察清单。
    • 反对意见和反例仅适用于有符号整数。无符号整数的行为,包括位和算术运算符之间的交互,完全由 C 语言定义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多