9

这是我昨天的问题的后续行动:

CMS 好心地提供了这个使用位运算符在 C 中添加两个数字的示例:

#include<stdio.h>

int add(int x, int y) {
    int a, b;
    do {
        a = x & y;
        b = x ^ y;
        x = a << 1;
        y = b;
    } while (a);
    return b;
}

int main( void ){
    printf( "6 + 3 = %d", add(6,3));
    printf( "6 - 3 = %d", add(6,-3));
    return 0;
}

它工作得很好,然后我将它移植到 Python,如下所示:

def add(x, y):
    while True:
        a = x & y
        b = x ^ y
        x = a << 1
        y = b
        if a == 0:
            break
    return b

print "6 + 3 = %d" % add(6,3)
print "6 - 3 = %d" % add(6,-3)

它们都适用于加法,C 程序也适用于减法。但是,Python 程序会进入一个无限循环进行减法。我试图深入了解并在此处发布程序以供进一步实验:http ://codepad.org/pb8IuLnY

谁能告诉我为什么 C 处理这个问题的方式和 CPython 处理这个问题的方式会有所不同?

4

4 回答 4

9

正如我在昨天对 CMS 的回答中指出的那样,左移一个负数是 C 中未定义的行为,所以这甚至不能保证在 C 中工作(问题是如何处理有符号位,你是否移动它像一个值位还是不受移位的影响?标准委员会无法就行为达成一致,因此未定义)。

当这恰好在 C 中起作用时,它依赖于固定位宽的整数,以便在您进行移位时最左边的位被推到末尾(它还需要将符号位视为值位以进行移位)。C 中的所有整数类型都是固定位的,但 Python 数字可以任意大。在 Python 中左移一个数字只会导致它不断变大:

>>> 1 << 100
1267650600228229401496703205376L

你可以尝试这样的事情:

x = (a << 1) & 0xffffffff

要将结果限制为 32 位,问题在于 Python 中的左移运算符不会移动有符号数的符号位(这是使此特定解决方案工作所需的一部分)。可能有一种方法可以改变班次操作员的行为,但我不知道如何。

于 2008-12-14T17:06:34.233 回答
2

移动负数在 python 和 C 之间没有一致的解释。

于 2008-12-14T17:04:34.223 回答
1

如果i,j是两个整数:

添加:

printf("%d",(i^j)|((i&j)<<1));
于 2010-08-15T13:43:38.657 回答
0

我注意到您假设 python 处理数字的方式与 C 相同。
这并不完全正确。这意味着 C 的 int 数具有 16 位的固定长度。有关 C 数据类型的详细信息,您可以参考en.wikipedia.org
上的 C_data_types 另一方面,据说对于 int 数而言,Python 的长度几乎是无限的。
添加正整数可能以相同的方式工作。但负整数的减法或加法不应该是简单的映射转换。
理解这一点的简单方法是一个关于负数的小例子:想象一个 3 位的固定长度整数表示:
#Unsigned#

  • 000: 0
  • 001: 1
  • 010: 2
  • 011: 3
  • 100: 4
  • 101: 5
  • 110: 6
  • 111: 7

#签:#

  • 000: 0
  • 001: 1
  • 010: 2
  • 011: 3
  • 100:-4
  • 101:-3
  • 110:-2
  • 111:-1

这很酷,因为您可以看到1-3=1+(-3),-3101如果未签名,则为 5。所以1+5=66 : 110: -2。这意味着1-3=-2.
溢出时也会出现问题:

  • -4 + -1 = 3不是-5,因为它超出了范围!
  • 3 + 1 = -4不是 4,因为它超出了范围!

正如您可能看到的那样,这适用于固定长度,但在 Python 中却不是这样。

于 2015-06-11T16:46:23.403 回答