【问题标题】:Why is 2 to the power of 31 returning a negative in this Java function?为什么在这个 Java 函数中 2 的 31 次方返回负数?
【发布时间】:2019-03-11 22:17:54
【问题描述】:

作业:

编写一个递归函数 recPow,在 Java 中当 n >= 0 时计算 2n。该函数将具有以下配置文件:

public static int recPow(int n)

函数必须考虑所有情况并进行详尽的测试。

我的问题

我不明白为什么当我输入 recPow(31) 而不是 2147483648 时我的代码返回 -2147483648。我知道你们中的一些人可能会告诉我切换到 long 而不是 int,但我相信由于作业的措辞我需要坚持int。如果有人能帮助我理解为什么会发生这种情况,我从来都不是很好的计算数字,我将非常感激。

此外 - 较大的指数返回 0(不过我认为这可能与我们需要使用整数与长整数有关。)

我的代码

public static int baseNum = 2, powResult = 1;

public static int recPow(int n) {
    //if the int is not bigger than 0 
    //must only accept ints
    if (n < 0) {
        throw new IllegalArgumentException("n has to be > 0");
    } else {
        //recursion here
        //base number = 2
        if (n==0) {
            return powResult;
        } else {
            powResult = powResult * baseNum;
            return recPow(n - 1);
        }
    }
}

【问题讨论】:

  • Java 中的最大 int 为 2147483647,因此您不能将 2147483648 的值表示为 int。我会尝试找到一个有用的副本。
  • 我认为超过 2^31 是因为 int 的位,例如 2^31 是最大的负数,它的二进制是 10000000,例如 8 位。据我所知,如果你将它乘以 2,你会将它向左移动一级,因此它为零(你只有 8 位)。
  • 嗨,谢谢你的回复,我明白了,也许我需要为这种情况做一些例外。对不起,我不明白你所说的有帮助的重复是什么意思?抱歉,这是什么?谢谢
  • @cdm89 "helpful dup" 是一个“很好解释的有用的重复问题”

标签: java tail-recursion pow negative-number


【解决方案1】:

较大的指数返回 0(但我认为这可能与我们需要使用 int 与 long 的事实有关。)

正确。

int i = (int) 2147483648L; // -2147483648 due to over flow
int j = i * 2; // 0 due to overflow.

您可以使用long,但这也有同样的问题,但价值更高。

public static long recPower(int baseNum, int power) {
    if (power < 0) throw new IllegalArgumentException();
    return power == 0 ? 1L : baseNum * recPower(baseNum, power - 1);
}

检查溢出的一种方法是查看

public static long recPower(int baseNum, int power) {
    if (power < 0) throw new IllegalArgumentException();
    return power == 0 ? 1L : baseNum * recPower(baseNum, power - 1);
}

或检查溢出

public static long recPower(int baseNum, int power) {
    if (power < 0) throw new IllegalArgumentException();
    return power == 0 ? 1L 
           : Math.multiplyExact(baseNum, recPower(baseNum, power - 1));
}

您可以使用 BigInteger,它的限制要大得多。

【讨论】:

  • 嗨,彼得,感谢您的回答,我非常感谢您的帮助,但是我认为对于此作业,我不能更改正在使用的类型,因为我们在作业中获得了配置文件。有没有办法做到这一点而不需要改变类型?我也希望你能解释你为什么要这样做 int j = i * 2?
  • 我不确定我是否完全理解条件语句。 “如果幂为 0 则返回 1L,否则为 multiplyExact() - 但我认为这应该是如果幂大于 30,您同意还是我完全误解了?再次感谢您的帮助
  • @cdm89 向您展示您得到的 0。每次乘以 0 时,您在数字移位的底部添加一个 0 位。添加 32 个零后,您将得到全 0。
  • @cdm89 如果您假设基数为 2,那么您是对的,对于更高的基数,您的限制会更低。
  • @cdm89 这有点像在问,我怎样才能将 10 月的天数翻倍,这样我才能在 10 月 60 日度过?您可以发明一种日历来实现这一点,或者发明一种 int 范围更广的语言,但您最好接受 10 月有 31 天并且 int 限制为 2^31-1。
【解决方案2】:

这是由于int 数据类型溢出造成的。

Java 的 int 大小为 32 位,因此范围是 -2,147,483,648 到 2,147,483,647。

2^31 = 2147483648

所以它溢出到-2147483648 因为 2,147,483,647 的二进制值是 01111111111111111111111111111111(一个 0 和 31 个 1),其中第一位是“符号位”(2 的补码形式)。

如果您尝试超出此限制 (2,147,483,647) 1(即向其添加 1),它会将符号位更改为 1,从而使 int 为负数。

所以它会变成 10000000000000000000000000000000(1 个 1 和 31 个零),答案是 -2147483648。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-22
    • 1970-01-01
    • 1970-01-01
    • 2016-03-21
    • 2018-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多