Double.NaN == Double.NaNがfalseを返すのはなぜですか?


155

私はOCPJPの質問を調べていたところ、次の奇妙なコードが見つかりました。

public static void main(String a[]) {
    System.out.println(Double.NaN==Double.NaN);
    System.out.println(Double.NaN!=Double.NaN);
}

私がコードを実行したとき、私は得ました:

false
true

false互いに同じように見える2つのものを比較すると、出力はどうなりますか?どういうNaN意味ですか?


8
これは本当に奇妙です。Double.NaNはstatic finalであるため、==との比較はtrueを返します。質問の+1。
Stephan

2
同じことは、Pythonで真である:In [1]: NaN==NaN Out[1]: False
TDC

58
IEEE 754標準に正しく準拠するすべての言語にも同じことが当てはまります。
zzzzBov

4
直観:「こんにちは」は数値ではなく、true(ブール値)も数値ではありません。NaN!=同じ理由でNaN「こんにちは」!= true
Kevin

3
@Stephan:との比較は、タイプがのDouble.NaN==Double.NaN場合、実際にtrueを返します。ただし、そのタイプはプリミティブであり、適用の演算子規則です(回答で説明されているように、IEEE 754に準拠するにはこの不等式が必要です)。Double.NaNjava.lang.Doubledoubledouble
sleske

回答:


139

NaNは「数ではない」を意味します。

Java言語仕様(JLS)の第3版は言う

オーバーフローする演算は符号付き無限大を生成し、アンダーフローする演算は非正規化値または符号付きゼロを生成し、数学的に明確な結果を持たない演算はNaNを生成します。NaNをオペランドとするすべての数値演算は、結果としてNaNを生成します。既に説明したように、数値比較演算には、1つのまたは2つのNaN戻り伴うので、NaNは、順不同でありfalse、任意!=のNaN戻りを伴う比較trueを含む、x!=x場合xNaNであるが。


4
@nibot:主に正しい。IEEE準拠のフロートと比較すると、が生成されfalseます。つまり、IEEEがそのことを要求しているという点で、その標準はJavaとは異なり(NAN != NAN) == falseます。
Drew Dormann 2012年

2
このPandoraの箱を開くと、「IEEEが(NAN!= NAN)== falseを要求する」とどこでわかりますか?
スーパーバイザー、

62

NaNは、定義上、NaNを含むどの数とも等しくありません。これはIEEE 754標準の一部であり、CPU / FPUによって実装されます。これは、JVMがサポートするロジックを追加する必要があるものではありません。

http://en.wikipedia.org/wiki/NaN

NaNとの比較は、それ自体と比較した場合でも、常に順序付けされていない結果を返します。...等式と不等式の述語はシグナルを送信しないため、x = xがfalseを返すことを使用して、xが静かなNaNかどうかをテストできます。

JavaはすべてのNaNをクワイエットNaNとして扱います。


1
それはCPUによって実装されていますか、それともボヘミアンが言及しているようにJVMに組み込まれていますか?
Naweed Chougle

3
JVMはそれを正しく実装するものを何でも呼び出す必要があります。PCでは、CPUがすべての作業を行います。これをサポートしていないマシンでは、JVMが実装する必要があります。(私はそのようなマシンのことは知りません)
ピーター・ローリー

8087がオプションだった当時、CライブラリにはFPエミュレータが含まれていました。JVMのようなプログラムは、どちらの方法でも心配する必要はありません。
ローンの侯爵

49

なぜそのロジック

NaNを意味しNot a Numberます。数字とは何ですか?なんでも。あなたは片側に何でも持つことができ、反対側に何でも持つことができるので、両方が等しいことを保証するものは何もありません。NaNで計算さDouble.longBitsToDouble(0x7ff8000000000000L)れ、のドキュメントで確認できるようにlongBitsToDouble

引数が範囲内の任意の値である場合0x7ff0000000000001Lを介して 0x7fffffffffffffffL又は範囲0xfff0000000000001Lを介して 0xffffffffffffffffL、結果はNaN

また、NaNAPI内で論理的に処理されます。


ドキュメンテーション

/** 
 * A constant holding a Not-a-Number (NaN) value of type
 * {@code double}. It is equivalent to the value returned by
 * {@code Double.longBitsToDouble(0x7ff8000000000000L)}.
 */
public static final double NaN = 0.0d / 0.0;

ところで、NaN され、あなたのコードサンプルとして試験:

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

解決

あなたができることはcompare/を使うことですcompareTo

Double.NaNこのメソッドでは、それ自体と等しく、他のすべてのdouble値(を含むDouble.POSITIVE_INFINITY)よりも大きいと見なされます 。

Double.compare(Double.NaN, Double.NaN);
Double.NaN.compareTo(Double.NaN);

またはequals

thisとのargument両方が表す場合Double.NaNequalsメソッドは値を持っtrueていますが、 戻りDouble.NaN==Double.NaNますfalse

Double.NaN.equals(Double.NaN);

NaN != NaN偽りであることは、NaN != NaN真実であることよりもプログラムが複雑になるケースを知っていますか?IEEEが何年も前に決定を下したことは知っていますが、実用的な観点からは、IEEEが役立つケースを見たことがありません。連続する反復が同じ結果をもたらすまで操作が実行されると想定されている場合、2つの連続する反復がNaNをもたらすことは、その動作ではない場合、終了条件として「自然に」検出されます。
スーパーキャット2013

@supercat 2つのランダムな非数が自然に等しいとどうして言えますか?または、基本的に等しいですか?NaNは、原始的なものではなく、インスタンスとして考えてください。それぞれの異常な結果は奇妙なものの異なるインスタンスであり、両方が同じものを表す必要がある場合でも、異なるインスタンスに==を使用するとfalseが返されます。一方、equalsを使用する場合は、意図したとおりに適切に処理できます。[ docs.oracle.com/javase/7/docs/api/java/lang/...
falsarella

@falsarella:問題は、2つの乱数を「完全に等しい」と見なすべきかどうかではなく、どのような場合でも、任意の数値をそれ自体と「完全に等しくない」と比較すると便利です。1が限度を計算しようとしている場合はf(f(f...f(x)))、もう1つは見つけたy=f[n](x)いくつかのためnの結果があるようf(y)区別がつかないyし、yこれ以上、深くネストされた結果と区別できないだろうf(f(f(...f(y)))NaN==NaN偽であることを望んでNan!=Nan 、偽であることはx!=x、一部のxに対して真であることよりも「驚くべきこと」ではありません。
スーパーキャット2013

1
@falsarella:のタイプはDouble.NaNでないと思いますDoubledouble、ので、問題はの動作に関連するものですdoubledouble値を含む同値関係をテストできる関数は存在しますが、「なぜ」(元の質問の一部です)について私が知っている唯一の説得力のある答えは、「IEEEの一部の人々は、等価テストが同値関係を定義します。」ところで、プリミティブ演算子のみを使用して等価性をテストxおよび簡潔にする慣用的な方法はありますyか 私が知っているすべての処方はかなり不格好です。
スーパーキャット2013

1
最良かつ簡単な答え。ありがとう
Tarun Nagpal

16

質問に対する直接の回答ではない可能性があります。しかし、何かが等しいかどうかを確認したいDouble.NaN場合は、これを使用する必要があります。

double d = Double.NaN
Double.isNaN(d);

これは戻ります true


6

Double.NaNjavadocはそれをすべて言います:

タイプの非数(NaN)値を保持する定数double。これは、によって返される値と同等Double.longBitsToDouble(0x7ff8000000000000L)です。

興味深いことに、defineのソースDoubleは次のNaNようになります。

public static final double NaN = 0.0d / 0.0;

説明する特別な動作は、JVMに組み込まれています。


5
それはJVMに組み込まれていますか、それともピーターが述べているようにCPUによって実装されていますか?
Naweed Chougle

4

あたりとして、浮動小数点演算のためのIEEE標準倍精度数のために、

IEEE倍精度浮動小数点標準表現には、64ビットワードが必要です。これは、左から右に0〜63の番号で表すことができます。

ここに画像の説明を入力してください どこ、

S: Sign  1 bit
E: Exponent  11 bits
F: Fraction  52 bits 

場合はE=2047(すべてがEある1)とFゼロで、その後、V=NaN(「数字ではありません」)

つまり、

すべてのEビットが1で、ゼロ以外のビットが含まれているF場合、数値はNaNです。

したがって、とりわけ、以下の数値はすべてNaN

0 11111111 0000000000000000010000000000000000000000000000000000 = NaN
1 11111111 0000010000000000010001000000000000001000000000000000 = NaN
1 11111111 0000010000011000010001000000000000001000000000000000 = NaN

特に、テストすることはできません

if (x == Double.NaN) 

特定の結果がと等しいかどうかを確認しDouble.NaNます。これは、すべての「数値ではない」値は別個のものと見なされるためです。ただし、次のDouble.isNaN方法を使用できます。

if (Double.isNaN(x)) // check whether x is "not a number"

3

NaNは「数値ではない」ことを示す特別な値です。これは、などの特定の無効な算術演算の結果でsqrt(-1)あり、(ときどき煩わしい)プロパティがありNaN != NaNます。


2

数値ではないというのは、その結果が数値で表現できない操作の結果を表します。最も有名な操作は0/0で、その結果は不明です。

このため、NaNは何にも等しくありません(他の非数値を含む)。詳細については、ウィキペディアのページを確認してください:http : //en.wikipedia.org/wiki/NaN


-1:しないの結果を表し0/00/0は常にNaNですが、@ AdrianMitevによる回答のように、NaNは2+NaN:などの他の演算の結果になる可能性がありますan operation that has no mathematically definite result produces NaN
ANeves

実際、NaNは「Not a Number」の略であり、結果として未定義または表現不可能な値を持つすべての演算の結果です。最も有名で一般的な操作は0/0ですが、同じ結果を持つ他の操作がたくさんあります。私の回答は改善される可能性があることには同意しますが、-1には同意しません... WikipediaもNaN結果を伴う最初の操作例として0/0操作を使用することを確認しました(en.wikipedia.org/wiki/ NaN)。
Matteo

また、これはDoubleのJavaソースにあります。public static final double NaN = 0.0d / 0.0;
ギヨーム

1
@Matteo +0、これで誤ったステートメントはなくなりました。そして、私の-1または+1は、あなたが同意したり反対したりするためのものではありません。しかし、コメントに-1を付けておくと、作者は自分の回答がなぜ役に立たないと見なされるのかを理解でき、必要に応じて変更できます。
ANeves 2012年

@Guillaumeそのコメントが私のためのものであった場合は、言い換えてください:私はそれを理解していません。
ANeves

0

このリンクによると、さまざまな状況があり、覚えにくいです。これは私がそれらを覚えて区別する方法です。NaNたとえば、「数学的に未定義」を意味します。たとえば、「0を0で割った結果は未定義」であり、未定義であるため、「未定義に関する比較はもちろん未定義」です。その上、それはより数学的な前提のように機能します。一方、正と負の両方の無限は事前定義され、決定的です。たとえば、「正または負の無限大は数学的に明確に定義されています」。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.