左移的负数*总是*用"1"而不是"0"填充吗?

Are negative numbers that are left shifted *always* filled in with "1" instead of "0"?

本文关键字:填充 总是 左移      更新时间:2023-10-16

我正在通过将一些c++函数移植到。net的BigInteger来独立研究位移位。我注意到,当我移动BigInteger时,空白被1填充。

我认为这与负数以双互补形式存储有关。

BigInteger num = -126;
compactBitsRepresentation = (uint)(int)(num << 16);

下面是移位后的结果(最有效位优先)

10000010 will be shifted 16
11111111100000100000000000000000 was shifted 16

我应该总是期望类似的位移操作以这种方式进行吗?这是否与OpenSSL等"bignnumber"的不同语言和实现一致?

来自BigInteger.LeftShift操作符文档:

与整型元的按位左移操作不同LeftShift方法保留原来BigInteger值的符号。

所以。net保证了你看到的行为。

我对bignum库不太熟悉,但是OpenSSL的bignum BN_lshift() '函数的文档说:

BN_lshift()将a左移n位,并将结果放入r ("r=a*2^n")。BN_lshift1()将a向左移动1,并将结果放入r("r=2*a")。

由于该操作是根据2的乘方定义的,如果将结果BIGNUM转换为2的补数(我不知道BIGNUM内部如何表示数字),那么您将看到与。net类似的行为。

如果其他bigum库有类似的行为,我不会感到惊讶,但如果你想依赖于这种行为,你真的需要检查文档。然而,由于移位非常类似于乘法或除以2的幂,您可能可以通过使用适当的乘法或除法而不是移位来获得"可移植"的行为。然后,您需要确保的是可以将其转换为二进制补码表示(这是一个真正独立于移位操作行为的问题)。

我应该总是期望类似的位移操作以这种方式进行吗?

如果有问题的数字格式使用2的补码来表示负数(很多都是这样),那么应该这样做。要形成一个数字的二补数,你要把所有的位反转并加一,所以例如:

23 is represented as 00010111
-23 is represented as 11101001 (that is, 11101000 + 1)

此外,当您将类型转换为较大类型时,该值通常是符号扩展的,即最左边的位扩展为较大类型中的额外位。保留数字的符号。

所以,是的,数值表示用1"填充"是很常见的。

10000010首先改为更大的宽度。在本例中,为4字节:

10000010 --> 11111111 11111111 11111111 10000010

左边是1,因为数字是负数

现在左移只是从右边插入0,从左边插入位:

11111111 11111111 11111111 10000010 << 16 -->
11111111 10000010 00000000 00000000