【问题标题】:Why is dividing by zero an unrecoverable issue?为什么除以零是一个不可恢复的问题?
【发布时间】:2014-01-22 07:55:38
【问题描述】:

为什么 java 不能优雅地返回一些除以零的值,而必须抛出异常?

我收到ArrayIndexOutOfBoundsException:0 这是因为damageTaken 实际上是一个存储许多不同“伤害”的值数组。

在 java 中,我正在尝试创建一个进度条。我们的示例:在赛车游戏中,通过将高度值设置为游戏结束前允许的最大伤害的百分比来造成伤害。

程序开始处damageTaken = 0;

(damageTaken / maximumDamage)

将给出 0 - 1 之间的数字。

然后我将它乘以进度条的高度,以创建一个适当高度的填充条。

程序崩溃。我希望进度条的高度为零!

【问题讨论】:

  • 它应该在 maximumDamage 为零而不是 damageTaken 时“崩溃”。对吗?
  • 检查 maximumDamage(不是damageTaken)不为零,这可能是问题的根源。
  • no maximumDamage 在我的实际示例中只是一些任意值,它是 16
  • 那么除以 null 发生在其他地方;介意分享更多代码吗?
  • @Arif:在这种情况下,您的代码不会导致崩溃。 0/16 = 0。如果您看到崩溃,它在其他代码中,或者maximumDamage 实际上 0。

标签: java processing divide-by-zero


【解决方案1】:

你不是除以零,你是在除以零

完全允许取零的两半。答案是零。假设你有零个苹果。你把零苹果分给 Alice 和 Bob。爱丽丝和鲍勃现在都拥有零个苹果。

但是,你不能除以零。假设你有两个苹果。现在,你想把这些苹果给零个人。每个人得到多少个苹果?答案是不确定的,所以除以零是不可能的。

【讨论】:

  • 更简单的是,假设如果存在 x 使得 0*x = 1,那么我们有一个直接矛盾,因为 0 = 0*x = 1。
  • 是的。如果允许除以零,则必须存在乘法逆元。例如,如果您声称 6 除以 2 等于 3,您可以证明它,因为 3*2 = 6。但您不能说 6 除以 0 等于 3,因为 3*0 = 6 永远不会为真。在微积分中,您可能会将 1/x 的极限视为无穷大,但这在现实世界中是行不通的。
  • 即使在微积分中它也不起作用,除非你对你的极限非常特别。假设您的拓扑结构可以区分它们,复平面中的每个接近方向都会产生不同的限制。这会打开一个全新的蠕虫罐头。
【解决方案2】:
(damageTaken / maximumDamage)

仅当maximumDamage 为零时,这会给您一个除零异常。

如果damageTaken 为零,则没有问题。

【讨论】:

  • 恭喜您成为唯一知道分子和分母区别的回答者。
【解决方案3】:

只需为 0 添加一个特殊情况;

private int getProgress()
    if (damageTaken == 0) {
        return 0;
    } else {
        return (damageTaken / maximumDamage) * progress.getHeight();
    }
}

然而,(但它很大)你得到除以 0 的原因是因为 maximumDamage 是 0,不是 damageTaken。所以,你可能真正想要的是:

private int getProgress()
    if (maximumDamage== 0) {
        return 0;
    } else {
        return (damageTaken / maximumDamage) * progress.getHeight();
    }
}

【讨论】:

  • 为什么要乘以进度条的高度?我理解他想要根据百分比值设置进度条高度的方式(所以它是一个垂直进度条)。
  • @poke,他想根据伤害百分比和整个栏的当前高度来了解进度需要多远。
  • 第一个建议的技术“0 的特殊情况”不会对已经发生的事情增加任何内容,如果您只是评估表达式。无论哪种情况,结果都是零。
【解决方案4】:

从概念上讲,让 4/0 产生一些任意数字不会比尝试将 2000000000 的计数加倍产生 -294967296 的计数更糟糕。然而,大多数处理器将忽略大多数类型的算术溢出,除非明确检查它,但不能忽略除以零的尝试,除非事先明确检查操作数(如果它们无效则跳过操作)。鉴于许多处理器都有“溢出”标志,没有什么可以阻止处理器指定尝试被零除应该只是设置溢出标志(成功的除法操作应该清除它);想要在这种情况下触发异常的代码可以这样做。

我怀疑这种不同行为的原因源于早期的计算;当余数小于除数时,除法指令的硬件可以判断它是完整的。如果这种情况从未发生过,则指令可能会停止,直到通用监控时钟电路(如果出于某种原因停止执行指令时会发出故障信号)将其关闭。按照今天的标准,检测问题并在不停止 CPU 的情况下退出的硬件将是微不足道的,但在计算机由分立晶体管构建的时代,它更便宜,而且几乎同样有效,告诉程序员不要试图划分零,永远。

【讨论】:

    【解决方案5】:

    让我们停止谈论非法,就好像穿制服的数学警察要冲进房间一样。 ;) 对于这背后的数学推理,this related question 很有用。

    问一下为什么现代编程平台没有以某种标准方式我们处理除零错误是公平的 - 但要回答这个问题:Java,像大多数其他平台一样,当发生被零除时会引发错误。一般来说,您可以:

    • 之前处理:在进行除法之前检查要用作分母的变量是否为零
    • Handle after:可以捕捉被零除时抛出的异常并优雅地处理。

    请注意,作为一种好的做法,第二种方法仅在您不希望变量在正常系统操作下为零时才适用

    所以是的,您需要一个特殊情况来处理这种情况。欢迎编程。 ;)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-04-03
      • 1970-01-01
      • 1970-01-01
      • 2021-04-28
      • 1970-01-01
      • 1970-01-01
      • 2012-07-13
      相关资源
      最近更新 更多