【问题标题】:BigInteger unsigned left or right shiftBigInteger 无符号左移或右移
【发布时间】:2011-03-12 10:02:46
【问题描述】:

我正在 int 中使用 BigInteger 重新实现一个函数。现在有步骤

h = n >>> log2n--

但是我在这里遇到了麻烦。原代码中 h、n、log2n 都是 int 类型,如果我将 h、n、log2n 设置为 BigInteger,上面代码的等价表达式是什么?如何在 BigInteger 中执行无符号右移 (>>>)?
编辑: 代码块是:

int log2n = 31 - Integer.numberOfLeadingZeros(n);
    int h = 0, shift = 0, high = 1;

    while (h != n)
    {
        shift += h;
        h = n >>> log2n--;
        int len = high;
        high = (h & 1) == 1 ? h : h - 1;
        len = (high - len) / 2;

        if (len > 0)
        {
            p = p.multiply(product(len));
            r = r.multiply(p);
        }
    }

【问题讨论】:

  • 你知道Java没有运算符重载,对吧?
  • 是的。我不是说运算符重载。是不是有什么迂回的方式或方法或算法可以找到无符号移位操作?

标签: java biginteger bit-shift


【解决方案1】:

引用 Java 文档:

无符号右移运算符 (>>>) 被省略,因为这个操作 与 “无限字长”抽象 由此类提供。

-1 的 32 位整数表示是(二进制)

11111111 11111111 11111111 11111111

如果您对此使用带符号的右移运算符 (>>),您将得到 ​​p>

11111111 11111111 11111111 11111111 

即一样的东西。如果您对此使用无符号右移运算符,移动 1,您将得到 ​​p>

01111111 11111111 11111111 11111111.

但是 BigInteger 的长度是无限的。 BigInteger 中 -1 的表示理论上是

11111111 111... infinite 1s here..... 11111111

无符号右移运算符意味着您将 0 放在最左边的点 - 即无穷大。由于这没什么意义,因此省略了运算符。

至于您的实际代码,您现在需要做什么取决于周围的代码在做什么以及为什么为原始代码选择无符号移位。像

n.negate().shiftRight(log2n)

可能有效,但这完全取决于具体情况。

【讨论】:

  • 这没有回答问题
【解决方案2】:

我终于找到了一个解决方案,很糟糕,但它确实有效:

public BigInteger srl(BigInteger l, int width, int shiftBy) {
    if (l.signum() >= 0)
        return l.shiftRight(shiftBy);
    BigInteger opener = BigInteger.ONE.shiftLeft(width + 1);
    BigInteger opened = l.subtract(opener);
    BigInteger mask = opener.subtract(BigInteger.ONE).shiftRight(shiftBy + 1);
    BigInteger res = opened.shiftRight(shiftBy).and(mask);
    return res;
}

你的整数是正数的情况是微不足道的,因为 shiftRight 无论如何都会返回正确的结果。但是对于负数,这变得很棘手。前面提到的否定版本不起作用,因为 BigInteger 中的 -1 否定是 1。将它移一下,你就有 0。但是你需要知道你的 BigInteger 的宽度是多少。然后,您基本上通过减去开瓶器来强制 BigInteger 至少具有 width+1 位。然后你执行移位,并屏蔽掉你引入的额外位。只要不改变低位,您使用什么开启器并不重要。

开瓶器的工作原理:

BigInteger 实现只存储负数的最高 0 位置。 A -3 表示为:

1111_1111_1111_1111_1101

但只存储了一些位,我将其他位标记为 X。

XXXX_XXXX_XXXX_XXXX_XX01

向右移动没有任何作用,因为总是有 1 来自左侧。所以想法是减去一个 1 以在您感兴趣的宽度之外生成一个 0。假设您关心最低 12 位:

XXXX_XXXX_XXXX_XXXX_XX01
-    0001_0000_0000_0000
========================
XXXX_XXX0_1111_1111_1101

这迫使生成真正的 1。然后你向右移动让我们说 5。

XXXX_XXX0_1111_1111_1101
>>5   XXXX_XXX0_1111_111

然后屏蔽它:

XXXX_XXX0_1111_111
0000_0000_1111_111

并因此得到正确的结果:

0000_0000_1111_111

因此,0 的引入迫使 BigInteger 实现将存储的 0 位置更新为比您感兴趣的位置更高的宽度,并强制创建存储的 1。

【讨论】:

  • 仅供参考,该问题是在 2 年前提出的,并且已接受答案
  • 我还是不明白你为什么要从 l 中减去一些 2^(width+1)。只是移位然后掩盖新位还不够吗?
  • 我添加了一些解释,对你有帮助吗?
【解决方案3】:

BigInteger 类有以下操作

 BigInteger     shiftLeft(int n)

 BigInteger     shiftRight(int n)

【讨论】:

  • @Tapas:不,他们不是,请查看 BigInteger 类的 Javadocs。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-10
  • 2013-01-03
  • 1970-01-01
相关资源
最近更新 更多