怎么可能“ while(i == i);” 是单线程应用程序中的非无限循环?


141

我有一个我无法回答的问题。

假设您在Java中具有以下循环定义:

while (i == i) ;

如果循环不是无限循环并且程序仅使用一个线程,则的类型i和值什么?i


7
哦,老天爷,人们是否有幸对他们为什么投票表示意见?这被标记为一个谜语,这是什么问题???
user54579

10
我认为有些人无法停止对任何事情的投票。
FerranB

1
Bah,很抱歉造成这个问题://我真的只是想要答案,但我自己也解决不了。
Zizzencs

1
@nickolai,这就是SO的本质-不幸的是,逐行否决是现实,要求评论的想法已被讨论并被拒绝。但是,看起来“社区”毕竟支持这个问题。
paxdiablo

1
这种问题的窍门之一是,人们假定某些变量名的类型,即,i是一个int,d是一个双精度,s是一个字符串或短整数,ch是一个字符,l是一个长整数,b是字节或布尔值。您必须问自己建议的类型是什么,可能是什么。
彼得·劳瑞

Answers:


125
double i = Double.NaN;

Double.equals()的API 给出了答案:“ Double.NaN == Double.NaN的值为false”。Java语言规范中“ 浮点类型,格式和值 ” 下对此进行了详细说明:

NaN是无序的,所以数值比较运算<<=>,和>= 返回false如果任一或两个操作数都NaN。等于运算符==返回false如果操作数是NaN和不平等运营商!=的回报true,如果一个操作数的NaN尤其x!=xtrue当且仅当xISNaN,而(x<y) == !(x>=y)将是false,如果xyis NaN


1
哦,所以你可以做“不是数字”!
Filip Ekberg

12
它在数学上是可靠的,为什么一个虚数等于另一个?5/0!= sqrt(-4)
Gordon Gustafson 2009年

2
@CrazyJugglerDrummer:也许吧,但同样x == x应该永远是对的。为什么有什么不等于自身?
Bart van Heukelom

1
巴特:因为真正的未知并不总是等于未知。有时它很有用,这就是数据库具有NULL的原因...
Konerak 2012年

2
@inovaovao:否,在数据库中null=null为空。NULL IS NULL是1
Konerak

29

的值i然后是无效的。“不是数字”。

经过一番谷歌搜索后,我发现您可以在Java中使用NaN(不是数字)!因此,浮点数是数据类型,值是NaN。看这里


12
是的,就是这样。i的值为Jon Skeet。
安德鲁·罗林斯

我通常讨厌那些无缘无故投降的人……我是第一个说“ Not a Number”的人,但是我不认为Java可以处理它,因为它不能处理其他任何很酷的事情。
菲利普·埃克伯格



8

我不确定,但是我相信(i == i)在多线程进程中不是原子操作,因此,如果在其他线程将其值推入执行循环的线程之间进行堆叠时,i值将被其他线程更改,则该条件可以是假的。


8

由于其他人说这是NaN,所以我对的正式(JDK 6)实现感到好奇Double.isNaN,请注意:

/**
 * Returns <code>true</code> if the specified number is a
 * Not-a-Number (NaN) value, <code>false</code> otherwise.
 *
 * @param   v   the value to be tested.
 * @return  <code>true</code> if the value of the argument is NaN;
 *          <code>false</code> otherwise.
 */
static public boolean isNaN(double v) {
    return (v != v);
}

2

将Nan视为异常的等效项,但在计算中使用魔术值。由于计算失败-例如负数的平方根,零除等,因此将它们与其他任何内容进行比较都没有意义。毕竟,如果被零除是一个nan,它等于-2的平方根还是-3的平方根?

Nan允许进行的计算包括返回无效答案的步骤以完成操作而不会引入额外的异常。要验证答案是否有价值,只需通过Float.isNan()o等效项测试非nandness(如果我不打包它就是这个词)。


3
如果我实际上知道什么是例外,那将它更容易地认为是例外。
paxdiablo,

2

我会补充

float i = Float.NaN;

以及

double i = Double.NaN;

在这类问题中,一个常见的技巧是假设您将i设为int。其他常见的假设可能是s是一个字符串,x,y是一个双精度型,ch是一个字符,b是一个字节,等等。如果您看到类似这样的问题,您可以打赌'i'不是它的期望类型。

一个类似的问题是;这永远不会循环,什么是“ x”

while(x == x && x != x + 0) { }

我很喜欢的另一个问题是:这个循环是一个无限循环,x的可能值是多少。(:我数了十二个:)

while(x != 0 && x == -x) { }

0

我知道这是一个Java问题,但是考虑其他语言的问题很有趣。

在C语言中,如果将“ i”声明为volatile,则诸如“ int”之类的简单类型可能表现出“在宇宙变冷之前终止”的行为(因此,编译器将被迫对每次迭代进行两次“ i”的读取)如果“ i”实际上在内存中,则可能有其他因素影响它。然后,当一次迭代的两次读取之间的“ i”发生变化时,循环将终止。(添加:一个可能的地方-在微型计算机中,'i'实际上位于I / O端口的地址上,也许连接到位置传感器。如果'i'是指针变量(可能更合理(指向易失性内存的指针),并且该语句为' while (*i == *i);'。)

正如其他答案所证明的那样,在C ++中,如果i是用户定义的类,则用户可以提供'=='运算符,因此一切皆有可能。

就像NaN一样,在基于SQL的语言中,如果i的值为NULL,则循环不会是无限的。但是,任何非NULL值都会使循环无限。这就像Java,其中任何数字(与NaN相对)使循环无限。

我看不到该构造有任何实际用途,但这是一个有趣的琐事问题。


0

我很惊讶没有看到这个解决方案:

while (sin(x) == sin(x)) //probably won't eval to true

为了回应评论,请尝试运行以下命令:

double x = 10.5f;
assert (x == asin(sin(x)));

从理论上讲,x应始终等于反正弦(sin(x)),但实际上并非如此。


3
为什么不?您对完全相同的数据执行完全相同的计算,两个结果将包含完全相同的误差。
罗伦·佩希特尔

我记不清确切的阅读位置,但是根据您的机器/实现,一个函数调用中的sin(x)与其他调用中的sin(x)相等的机会很小。它与浮点数的准确性有关(trig函数的某些特殊之处,使它们不会两次返回相同的值)。
jkeys

查看我的更新。反正弦可以“撤消” x的罪过,因此它们应该相等,但不相等。
jkeys

那不是同一回事。就像Loren所说的那样,您正在对执行完全相同的操作x,这将产生具有完全相同的准确性的完全相同的结果。Arcsin不能将带有浮点数的sin的结果取反,因为传递给的值将asin()不完全准确。因此,的结果asin()将是不准确的,从而导致x == asin(sin(x))错误。另外,arcsin不一定会“撤消” sin操作-sin函数可以为x的多个值给出相同的结果,这就是为什么asin()仅返回-π/ 2和π/ 2之间的数字的原因。
hbw

2
换句话说,arcsin不能总是“撤消” sin函数,因为arcsin不可能知道原始角度是多少。例如,π/ 2和5π/ 2的sin均为1。但是arcsin(1)是什么?显然不能两全其美,对吗?因此,arcsin的结果必须限制在2π弧度的范围内,这意味着除非原始角度在0到2π之间,或者在C的情况下为-π/ 2和π/ 2。(实际上,arcsin(sin(5π/ 2))=π/ 2。)不管怎么说,这是一个很长的解释,但是我希望这可以消除任何误解。
hbw

0

不是无限循环,一个线程:)

import static B.*;
public class A {
    public static void main(String[] args) {
        System.out.println("Still Running");
        while (i == i) ;
    }
}


public class B {

    public static int i;
    static {
        System.exit(0);
    }
}

-1

i == i不是原子的。通过这样的程序证明:

static volatile boolean i = true;
public static void main(String[] args) throws InterruptedException
{
    new Thread() {
        @Override
        public void run() {
            while (true) {
                i = !i;
            }
        }
    }.start();

    while (i == i) ;
    System.out.println("Not atomic! i: " + i);
}

更新 这是非无限循环的另一个示例(不创建新线程)。

public class NoNewThreads {
    public static void main(String[] args) {
        new NoNewThreads();
        System.gc();
        int i = 500;
        System.out.println("Still Running");
        while (i == i) ;
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        Thread.sleep(1000);
        System.exit(0);
    }
}

@Charles Goodwin您所说的事实是不可能使用一个线程来编写Java程序:),因此所有其他解决方案都至少使用两个线程(与“更新”部分中的第二个程序完全相同)。
安德烈

那并没有阻止循环。之后的任何代码都while (i == i);不会执行。
2011年

Finalize使用另一个线程,但要指出这一点是一件好事。这就是原始问题说“该程序仅使用一个线程”的原因。.明确指出这是显而易见的答案,他们希望您进一步研究。
Bill K
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.