【问题标题】:For what value of i does while (i == i + 1) {} loop forever?对于 i 的值,while (i == i + 1) {} 会永远循环吗?
【发布时间】:2019-04-30 04:02:52
【问题描述】:

I ran cross this puzzler from an advanced programming course at a UK university exam.

考虑以下循环,其中 i 目前尚未声明:

while (i == i + 1) {}

找到i的定义,在这个循环之前,这样while循环 永远持续下去。

下一个问题,对这段代码sn-p提出了同样的问题:

while (i != i) {}

对我来说很明显。当然,在另一种情况下,它是NaN,但我真的被困在前一种情况上。这与溢出有关吗?什么会导致这样的循环在 Java 中永远循环?

【问题讨论】:

  • 是否有可能覆盖.equals() 方法?由于 i 未声明,我们可以使用任何我们想要的类。
  • @Raedwald 学习“不专业”的代码会让你更“专业”,所以……无论如何,这是个好问题
  • 有趣的是,在 C# 中,这也适用于值为 null 的可空数值类型,因为 null == null 为真,null + 1null
  • @EricDuminil:情况比你想象的要糟糕得多。在许多语言中,浮点运算必须至少以 double 指定的 64 位精度完成,这意味着它可以随心所欲地以更高的精度完成编译器,在实践中会发生这种情况。我可以向您指出这个站点上来自 C# 程序员的十几个问题,他们想知道为什么 0.2 + 0.1 == 0.3 会根据编译器设置、月相等更改其值。
  • @EricDuminil:这个烂摊子的责任落在了英特尔身上,如果数字可以注册的话,它给了我们一个芯片组,它可以进行更高精度和 更快 浮点运算,这意味着浮点计算的结果可以根据优化器中的寄存器调度程序今天的工作情况来改变它们的值。作为语言设计者,你的选择是在可重复计算快速、精确计算之间,关心浮点数学的社区会选择后者。

标签: java loops types


【解决方案1】:

首先,由于while (i == i + 1) {}循环不会改变i的值,所以使这个循环无限等于选择一个满足i == i + 1i值。

这样的价值观有很多:

让我们从“异国情调”的开始:

double i = Double.POSITIVE_INFINITY;

double i =  Double.NEGATIVE_INFINITY;

这些值满足i == i + 1 的原因在
JLS 15.18.2. Additive Operators (+ and -) for Numeric Types 中说明:

无穷大和有限值之和等于无穷大操作数。

这并不奇怪,因为将有限值添加到无限值应该会产生无限值。

也就是说,满足i == i + 1i 的大多数值都只是较大的double(或float)值:

例如:

double i = Double.MAX_VALUE;

double i = 1000000000000000000.0;

float i = 1000000000000000000.0f;

doublefloat 类型的精度有限,因此如果您采用足够大的 doublefloat 值,将 1 添加到它会得到相同的值。

【讨论】:

  • (double)(1L<<53) -- 或float i = (float)(1<<24)
  • @Ruslan:任何数学家都会不同意。浮点数没有什么意义。它们是非关联的、非自反的(NaN != NaN),甚至不可替代(-0 == 0,但 1/0 != 1/-0)。所以代数的大部分机制是不适用的。
  • @Kevin 虽然浮点数在一般情况下确实不太有意义,但无穷大的行为(这句话中描述的内容)被设计为有意义。
  • @Kevin 为了公平对待浮点数,如果您处理无穷大或未定义的值,您也不能假设您在代数中列出的属性。
  • @Kevin:恕我直言,如果浮点数学将“正负零”符号正、负和无符号“无穷小”的概念替换为一个“真零”,并使 NaN 等于它自己。在所有种情况下,真零可以作为一个加法恒等式,并且涉及被无穷小除法的某些操作将失去对假设无穷小为正的偏见。
【解决方案2】:

Joshua Bloch 和 Neal Gafter 所著的“Java Puzzlers: Traps, Pitfalls, and Corner Cases”一书中详细描述了这些谜题。

double i = Double.POSITIVE_INFINITY;
while (i == i + 1) {}

或:

double i = 1.0e40;
while (i == i + 1) {}

两者都会导致无限循环,因为将1 添加到足够大的浮点值不会改变该值,因为它不会“弥合差距”到其后继1.

关于第二个谜题的说明(供未来的读者使用):

double i = Double.NaN;
while (i != i) {}

也会导致无限循环,因为NaN不等于任何浮点值,包括它自己2


1 - Java Puzzlers:陷阱、陷阱和角落案例(第 4 章 - Loopy Puzzlers)。

2 - JLS §15.21.1

【讨论】:

    【解决方案3】:

    double i = Double.POSITIVE_INFINITY;

    【讨论】:

      【解决方案4】:

      只是一个想法:布尔值呢?

      bool i = TRUE;
      

      这不是i + 1 == i的情况吗?

      【讨论】:

      • 取决于语言。许多语言在与 int 结合时会自动将布尔值强制转换为 int。其他人按照你的建议做 - 将 int 强制为布尔值。
      • 这个问题是一个 Java 问题,您的建议没有通过 Java 编译(它没有将 booleanint 作为操作数的 + 运算符)。跨度>
      • @Eran:这就是运算符重载的全部想法。你可以让 Java 布尔值表现得像 C++ 的一样。
      • 只是Java不支持运算符重载,所以不能。
      猜你喜欢
      • 2011-06-14
      • 2012-10-26
      • 2012-01-13
      • 1970-01-01
      • 2019-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-09
      相关资源
      最近更新 更多