当(i == i + 1){}永远循环时,i的值是多少?


120

我在英国大学考试的高级编程课程中遇到了这个难题

考虑以下循环,到目前为止,我还没有声明过:

while (i == i + 1) {}

找到i在此循环之前的的定义,以便while循环永远持续下去。

下一个问题,针对此代码段询问了相同的问题:

while (i != i) {}

对我来说很明显 当然,在其他情况下也是如此,NaN但我确实被先前的情况所困扰。这和溢出有关吗?什么会导致这样的循环在Java中永远循环?


3
有否重写.equals()方法的可能性?由于我未声明,因此我们可以使用所需的任何类。
Geno Chen

7
@Raedwald学习“非专业”代码会使您更“专业”,所以...无论如何,这是一个好问题
Andrew Tobilko

12
有趣的是,在C#中,这也适用于可为空的数字类型,其值是null,因为它null == null是true,并且null + 1null
埃里克·利珀特

4
@EricDuminil:情况比您想象的要糟得多。在许多语言中,浮点算术必须至少以double所指定的64位精度来完成,这意味着可以在编译器心血来潮时以更高的精度来完成浮点算术,并且在实践中会发生这种情况。我可以为您指出该站点上来自C#程序员的十二个问题,他们想知道为什么要0.2 + 0.1 == 0.3根据编译器设置,月相等等来更改其值。
埃里克·利珀特

5
@EricDuminil:造成这种混乱的原因归咎于英特尔,英特尔给了我们一套芯片组,如果可以注册数字,它可以执行更高精度和更快的浮点运算,这意味着浮点计算的结果可以根据其值进行更改。当前优化器中的寄存器调度程序的运行情况。然后,您作为语言设计师的选择是在可重复计算快速,精确计算之间进行,而关心浮点数学的社区将选择后者。
埃里克·利珀特

Answers:


142

首先,由于while (i == i + 1) {}循环不会更改的值i,因此使该循环变为无限等效于选择i满足的值i == i + 1

有许多这样的值:

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

double i = Double.POSITIVE_INFINITY;

要么

double i =  Double.NEGATIVE_INFINITY;

这些值满足的原因i == i + 1
JLS 15.18.2中进行了说明。数值类型的加法运算符(+和-)

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

这并不奇怪,因为将无限值添加到无限值将导致无限值。

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

例如:

double i = Double.MAX_VALUE;

要么

double i = 1000000000000000000.0;

要么

float i = 1000000000000000000.0f;

doublefloat类型有精度有限,因此,如果您需要一个足够大的doublefloat价值,加上1它会导致相同的值。


10
(double)(1L<<53)-或float i = (float)(1<<24)
dave_thompson_085 '18

3
@卢斯兰:任何数学家都会不同意。浮点数没有什么意义。它们是非关联的,非反身的(NaN!= NaN),甚至不可取代(-0 == 0,但1/0!= 1 / -0)。因此,大多数代数机器都不适用。
凯文

2
@Kevin虽然浮点数通常实际上并没有太大意义,但无限性的行为(即该语句中所描述的)被设计为有意义。
罗斯兰

4
@Kevin为了公平起见,如果您处理无穷大或未定义的值,则也不能假设您在代数中列出的属性。
Voo

2
@Kevin:恕我直言,如果将“正零和负零”的符号替换为正,负和无符号“无穷小”以及一个“真零”的概念,那么浮点数学本来就更有意义了,并且NaN等于其自身。真零在所有情况下可以充当加性标识,并且涉及除以无穷小数的某些运算将失去其假设无穷小为正的偏见。
超级猫

64

约书亚·布洛赫(Joshua Bloch)和尼尔·古夫特(Neal Gafter)所著的“ Java拼图者:陷阱,陷阱和陷阱”一书中详细描述了这些难题。

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益智游戏:陷阱,陷阱和极端案例(第4章-Loopy益智游戏)。

2- JLS§15.21.1



0

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

bool i = TRUE;

难道不是这样i + 1 == i吗?


取决于语言。与int结合使用时,许多语言会自动将布尔值强制转换为int。其他人按照您的建议执行-将int强制为布尔值。
卡尔·威索夫特

8
这个问题是一个Java问题,您的建议不会通过Java编译(它没有+采用a booleanintas作为操作数的运算符)。
伊兰(Eran)

@Eran:这就是操作符重载的全部思想。您可以使Java布尔型的行为类似于C ++。
多米尼克

4
Java不支持运算符重载,因此您不能。
CupawnTae
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.