ทำไม Double.NaN == Double.NaN ส่งคืนค่าเท็จ


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เมื่อเราเปรียบเทียบสองสิ่งที่ดูเหมือนกันหรือไม่ อะไรNaNหมายถึง?


8
นี่มันแปลกจริงๆ เนื่องจาก Double.NaN เป็นแบบสแตติกสุดท้ายการเปรียบเทียบกับ == ควรกลับเป็นจริง +1 สำหรับคำถาม
เตฟาน

2
เช่นเดียวกับในไพ ธ อน:In [1]: NaN==NaN Out[1]: False
tdc

58
เช่นเดียวกันในทุกภาษาที่ปฏิบัติตามมาตรฐาน IEEE 754 อย่างถูกต้อง
zzzzBov

4
ปรีชา: "สวัสดี" ไม่ใช่ตัวเลขจริง (บูลีน) ก็ไม่ใช่ตัวเลข NaN! = NaN ด้วยเหตุผลเดียวกัน "Hello"! = true
Kevin

3
@Stephan: การเปรียบเทียบกับDouble.NaN==Double.NaNแน่นอนควรกลับจริงถ้าเป็นประเภทDouble.NaN java.lang.Doubleอย่างไรก็ตามชนิดของมันเป็นแบบดั้งเดิมdoubleและกฎของตัวดำเนินการสำหรับการdoubleใช้ (ซึ่งต้องการความไม่เท่าเทียมกันนี้เพื่อให้สอดคล้องกับ IEEE 754 ดังที่อธิบายไว้ในคำตอบ)
sleske

คำตอบ:


139

NaN หมายถึง "ไม่ใช่ตัวเลข"

Java Language Specification (JLS) รุ่นที่สามพูดว่า :

การดำเนินการที่โอเวอร์โฟลว์สร้างอินฟินิตี้ที่ลงนามแล้วการดำเนินการที่อันเดอร์โฟลว์สร้างค่า denormalized หรือศูนย์ที่ลงนามและการดำเนินการที่ไม่มีผลลัพธ์ที่ชัดเจนทางคณิตศาสตร์ทำให้เกิด NaN การดำเนินการตัวเลขทั้งหมดที่มี NaN เป็นตัวถูกดำเนินการผลิต NaN เป็นผล ตามที่ได้อธิบายไปแล้ว NaN ไม่มีการจัดลำดับดังนั้นการดำเนินการเปรียบเทียบเชิงตัวเลขที่เกี่ยวข้องกับการส่งคืน NaN หนึ่งหรือสองครั้งfalseและ!=การเปรียบเทียบใด ๆ ที่เกี่ยวข้องกับการส่งคืน NaN trueรวมถึงx!=xเมื่อxเป็น NaN


4
@nibot: ส่วนใหญ่ที่แท้จริง การเปรียบเทียบใด ๆ กับ float ที่สอดคล้องกับมาตรฐาน IEEE จะสร้างfalseขึ้น เพื่อให้แตกต่างจากมาตรฐาน IEEE (NAN != NAN) == falseชวาที่เรียกร้องให้
Drew Dormann

2
การเปิดกล่องแพนโดร่านี้ - คุณเห็นว่า "IEEE เรียกร้องให้ (NAN! = NAN) == false" ที่ไหน
หัวหน้างาน

62

NaN มีความหมายไม่เท่ากับจำนวนใด ๆ รวมถึง NaN นี่เป็นส่วนหนึ่งของมาตรฐาน IEEE 754 และดำเนินการโดย CPU / FPU ไม่ใช่สิ่งที่ JVM ต้องเพิ่มตรรกะเพื่อสนับสนุน

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

การเปรียบเทียบกับ NaN จะส่งคืนผลลัพธ์ที่ไม่ได้เรียงลำดับเสมอแม้ว่าจะเปรียบเทียบกับตัวเองก็ตาม ... ความเท่าเทียมและความไม่เท่าเทียมกันของภาคแสดงเป็นสัญญาณที่ไม่ใช่ดังนั้น x = x กลับเท็จสามารถใช้เพื่อทดสอบว่า x เป็น NaN ที่เงียบ

Java ถือว่า NaN ทั้งหมดเป็นเงียบ ๆ NaN


1
มันใช้งานโดยซีพียูหรือมีสายอย่างหนักใน JVM ตามที่กล่าวถึงในภาษาโบฮีเมีย
Naweed Chougle

3
JVM ต้องเรียกสิ่งที่จะนำไปใช้อย่างถูกต้อง บนพีซี CPU ทำหน้าที่ดังกล่าวทั้งหมด บนเครื่องที่ไม่มีการสนับสนุน JVM จะต้องติดตั้ง (ผมไม่ทราบว่าของเครื่องดังกล่าว)
ปีเตอร์ Lawrey

ย้อนกลับไปในวันที่ 8087 เป็นตัวเลือกห้องสมุด C มีตัวจำลอง FP โปรแกรมอย่าง JVM ไม่ต้องกังวลกับมันทั้งสองทาง
มาร์ควิสแห่ง Lorne

49

ทำไมตรรกะนั้น

NaNNot a Numberวิธี หมายเลขอะไร สิ่งใด คุณสามารถมีอะไรในด้านหนึ่งและอะไรก็ได้ในอีกด้านหนึ่งดังนั้นจึงไม่มีอะไรรับประกันว่าทั้งสองจะเท่ากัน NaNคำนวณด้วยDouble.longBitsToDouble(0x7ff8000000000000L)และตามที่คุณเห็นในเอกสารประกอบของlongBitsToDouble:

ถ้าอาร์กิวเมนต์เป็นค่าใด ๆ ในช่วง0x7ff0000000000001Lผ่าน 0x7fffffffffffffffLหรืออยู่ในช่วง0xfff0000000000001Lผ่าน 0xffffffffffffffffL, NaNผลที่ได้คือ

นอกจากนี้ยังNaNได้รับการจัดการทางตรรกะภายใน API


เอกสาร

/** 
 * 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.NaNแล้วequalsวิธีการผลตอบแทนtrueแม้ว่า มีค่าDouble.NaN==Double.NaNfalse

Double.NaN.equals(Double.NaN);

คุณรู้กรณีที่มีการNaN != NaNปลอมจะทำให้โปรแกรมซับซ้อนกว่าการNaN != NaNเป็นจริงหรือไม่? ฉันรู้ว่า IEEE ตัดสินใจเมื่อหลายปีก่อน แต่จากมุมมองที่ใช้งานจริงฉันไม่เคยเห็นกรณีที่มันมีประโยชน์ หากการดำเนินการควรจะรันจนกว่าการวนซ้ำตามลำดับจะให้ผลลัพธ์เดียวกันการมีการวนซ้ำสองครั้งติดต่อกันทำให้ NaN ถูกตรวจพบว่า "เป็นธรรมชาติ" เนื่องจากเงื่อนไขการออกไม่ใช่สำหรับพฤติกรรมนั้น
supercat

@supercat คุณจะบอกได้อย่างไรว่าเลขสุ่มสองตัวนั้นเท่ากันโดยธรรมชาติ? หรือพูดอย่างเท่าเทียมกันดั้งเดิม? คิดว่า NaN เป็นตัวอย่างไม่ใช่สิ่งดั้งเดิม ผลลัพธ์ที่ผิดปกติแต่ละอย่างแตกต่างกันเป็นอินสแตนซ์ของบางสิ่งที่แปลกและแม้ว่าทั้งคู่ควรแสดงเหมือนกันการใช้ == สำหรับอินสแตนซ์ต่าง ๆ จะต้องส่งคืนค่าเท็จ ในทางกลับกันเมื่อใช้เท่ากับจะสามารถจัดการอย่างถูกต้องตามที่คุณตั้งใจ [ docs.oracle.com/javase/7/docs/api/java/lang/…
falsarella

@ falsarella: ปัญหาไม่ได้ว่าควรพิจารณาตัวเลขสุ่มสองตัวว่า "เท่ากันแน่นอน" แต่ในกรณีใดมันมีประโยชน์ที่จะมีตัวเลขใด ๆ ที่เปรียบเทียบว่า "ไม่เท่ากันอย่างแน่นอน" กับตัวเอง หากมีใครพยายามคำนวณขีด จำกัด ของf(f(f...f(x)))และหนึ่งพบว่าy=f[n](x)สำหรับบางnอย่างที่ผลมาจากการf(y)แยกไม่ออกจากyนั้นyจะแยกไม่ออกจากผลของการซ้อนกันลึกมากf(f(f(...f(y)))ขึ้น แม้ว่าใครอยากNaN==NaNจะเป็นเท็จมีNan!=Nan ยังเป็นเท็จจะน้อย "น่าแปลกใจ" กว่ามีx!=xเป็นจริงสำหรับบาง x
supercat

1
@falsarella: ผมเชื่อว่าประเภทของDouble.NaNไม่ได้Doubleแต่ดังนั้นคำถามที่เกี่ยวข้องกับพฤติกรรมหนึ่งของdouble doubleแม้ว่าจะมีฟังก์ชั่นที่สามารถทดสอบความสัมพันธ์ที่เทียบเท่าdoubleค่าได้ แต่คำตอบที่น่าสนใจที่ฉันรู้สำหรับ "ทำไม" (ซึ่งเป็นส่วนหนึ่งของคำถามเดิม) คือ "เพราะบางคนที่ IEEE ไม่คิดว่าการทดสอบความเท่าเทียมกัน กำหนดความสัมพันธ์ที่เท่าเทียมกัน ". BTW มีวิธีรัดกุมในการทดสอบxและความyเท่าเทียมกันโดยใช้ตัวดำเนินการดั้งเดิมเท่านั้นหรือไม่ สูตรทั้งหมดที่ฉันรู้จักนั้นค่อนข้างที่จะเป็นตัวหนา
supercat

1
คำตอบที่ดีที่สุดและง่าย ขอบคุณ
Tarun Nagpal

16

อาจไม่ใช่คำตอบสำหรับคำถามโดยตรง แต่ถ้าคุณต้องการตรวจสอบว่ามีอะไรที่เท่ากับDouble.NaNคุณควรใช้สิ่งนี้:

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

สิ่งนี้จะกลับมา true


6

Javadoc สำหรับ Double.NaNกล่าวมันทั้งหมด:

คงถือไม่-a-จำนวน (น่าน) doubleค่าของชนิด Double.longBitsToDouble(0x7ff8000000000000L)มันจะเทียบเท่ากับค่าส่งกลับโดย

น่าสนใจแหล่งที่มาของการDoubleนิยามNaNดังนี้:

public static final double NaN = 0.0d / 0.0;

พฤติกรรมพิเศษที่คุณอธิบายนั้นมีสายเข้ากับ JVM


5
เป็นสายแข็งใน JVM หรือใช้ CPU ตามที่ Peter กล่าวถึงหรือไม่
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/0หมายถึงผลมาจากการ 0/0อยู่เสมอ NaN แต่ NaN อาจเป็นผลมาจากการดำเนินการอื่น ๆ เช่น2+NaN: an operation that has no mathematically definite result produces NaNตามคำตอบของ @AdrianMitev
ANeves

แท้จริงแล้ว NaN ย่อมาจาก "Not a Number" และเป็นผลมาจากการดำเนินการทั้งหมดที่มีค่าที่ไม่ได้กำหนดหรือไม่สามารถใช้แทนได้ การดำเนินการที่มีชื่อเสียงและใช้กันทั่วไปคือ 0/0 แต่เห็นได้ชัดว่ามีการดำเนินงานอื่น ๆ มากมายที่มีผลลัพธ์เหมือนกัน ฉันเห็นด้วยว่าคำตอบของฉันควรได้รับการปรับปรุง แต่ฉันไม่เห็นด้วยกับ -1 ... ฉันเพิ่งตรวจสอบว่าวิกิพีเดียใช้การดำเนินการ 0/0 เป็นตัวอย่างแรกของการดำเนินการด้วยผลลัพธ์ NaN ( en.wikipedia.org/wiki/) NaN )
Matteo

นอกจากนี้ยังอยู่ในแหล่ง Java สำหรับ Double: public static final double NaN = 0.0d / 0.0;
Guillaume

1
@Matteo +0 ตอนนี้คำสั่งเท็จหายไป และ -1 หรือ +1 ของฉันไม่ได้ให้คุณเห็นด้วยหรือไม่เห็นด้วย; แต่เป็นการดีที่จะแสดงความคิดเห็นด้วย -1 เพื่อให้ผู้เขียนสามารถเข้าใจว่าทำไมคำตอบของเขาจึงถือว่าไม่เหมาะสม - และเปลี่ยนแปลงหากเขาต้องการ
Aneves

@Gillailla หากความคิดเห็นนั้นมีความหมายสำหรับฉันโปรดใช้ถ้อยคำใหม่: ฉันไม่เข้าใจ
Aneves

0

ตามลิงค์นี้มันมีสถานการณ์ต่าง ๆ และยากที่จะจำ นี่คือวิธีที่ฉันจำและแยกแยะพวกเขา NaNหมายถึง "คณิตศาสตร์ไม่ได้กำหนด" ตัวอย่างเช่น: "ผลลัพธ์ของ 0 หารด้วย 0 คือไม่ได้กำหนด" และเพราะมันไม่ได้กำหนดดังนั้น "การเปรียบเทียบที่เกี่ยวข้องกับไม่ได้กำหนดเป็นหลักสูตรที่ไม่ได้กำหนด" นอกจากนี้ยังใช้งานได้เหมือนสถานที่ทางคณิตศาสตร์ ในทางตรงกันข้ามทั้งบวกและลบอนันต์ถูกกำหนดไว้ล่วงหน้าและชัดเจนเช่น "บวกหรือลบอนันต์ขนาดใหญ่มีการกำหนดทางคณิตศาสตร์ที่ดี"

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.