ใน Java NaN หมายถึงอะไร?


108

ฉันมีโปรแกรมที่พยายามย่อdoubleตัวเลขที่ต้องการ NaNเอาท์พุทฉันได้รับคือ

อะไรNaNหมายถึงใน Java?


มีคำอธิบายที่ดีเกี่ยวกับ NaN และข้อผิดพลาดทั่วไปเมื่อใช้ NaN ใน Java: ppkwok.blogspot.co.uk/2012/11/…
Phil

หากคุณกำลังถามว่า "NaN คืออะไร" ใน Java (หรือภาษาอื่น ๆ ) ฉันสามารถให้กรณีการใช้งานที่มีประโยชน์มาก: เมื่อฉันมีอาร์เรย์แบบ 2 มิติ แต่การคำนวณของฉันไม่มีค่าที่มีความหมายสำหรับบางส่วนของอาร์เรย์ 2 มิตินั้น ฉันจะเติมค่านั้นด้วย "NaN" สิ่งนี้สามารถใช้เพื่อส่งสัญญาณให้ผู้ใช้ดาวน์สตรีมการคำนวณของฉัน (เช่นเมื่อมันกลายเป็นภาพแรสเตอร์) "อย่าใส่ใจกับค่า ณ จุดนี้" มีประโยชน์มาก!
Dan H

BTW หมายความว่าอย่างไรการ "ลดขนาด" เป็นสองเท่า อยากรู้อยากเห็น ...
Dan H

คำตอบ:


154

นำมาจากหน้านี้ :

"NaN" ย่อมาจาก "not a number" "น่าน" เกิดขึ้นหากการดำเนินการจุดลอยตัวมีพารามิเตอร์อินพุตบางอย่างที่ทำให้การดำเนินการสร้างผลลัพธ์ที่ไม่ได้กำหนดไว้ ตัวอย่างเช่น 0.0 หารด้วย 0.0 จะไม่ได้กำหนดทางคณิตศาสตร์ การหารากที่สองของจำนวนลบก็ไม่ได้กำหนดเช่นกัน


16
นอกจากนี้ NaN ยังถูกกำหนดโดยมาตรฐาน IEEE สำหรับ Floating-Point Arithmetic (IEEE 754) ค่อนข้างชัดเจนซึ่ง Java ติดตามสุ่มสี่สุ่มห้า การอ่านมาตรฐานเปิดโลกทัศน์ของคุณให้กับหลายสิ่งหลายอย่างค่าหลายค่าของศูนย์เป็นหนึ่งในสิ่งต่างๆ
Esko

37
นอกจากนี้ยังNaNมีคุณสมบัติที่น่าสนใจในการเป็น "ตัวเลข" เพียงตัวเดียวซึ่งไม่เหมือนกับตัวมันเองเมื่อเปรียบเทียบ ดังนั้นจึงเป็นการทดสอบทั่วไป (และในหลายภาษาเท่านั้น) หากตัวเลขxเป็นNaNดังต่อไปนี้:boolean isNaN(x){return x != x;}
quazgar

3
ลิงค์ในคำตอบตาย?
ปัง

3
... "การหารากที่สองของจำนวนลบไม่ได้กำหนด (ในวิชาเลขคณิต)" ... ไม่ใช่! ของจริงiและภาษาบางอย่างเช่นการจัดการหลามเป็นอย่างดีกับมัน ... มันอาจจะไม่ได้ในกรณีที่javaท่าน
ราฟาเอล T

5
@RafaelT ฉันจะบอกว่ามันไม่ได้กำหนดไว้ในเลขคณิตที่ไม่ซับซ้อน ไม่มีวิธีกำหนดจำนวนเชิงซ้อนให้กับ float หรือ double ใน Java Python ถูกพิมพ์แบบไดนามิกดังนั้นในกรณีนี้อาจส่งคืนจำนวนเชิงซ้อน
sstn

19

NaNหมายถึง“ ไม่ใช่ตัวเลข”และโดยพื้นฐานแล้วเป็นตัวแทนของค่าทศนิยมพิเศษในมาตรฐานจุดลอยตัว IEE 754 NaNโดยทั่วไปหมายความว่าค่าเป็นสิ่งที่ไม่สามารถแสดงด้วยตัวเลขทศนิยมที่ถูกต้อง

การแปลงจะทำให้เกิดค่านี้เมื่อค่าที่ถูกแปลงเป็นอย่างอื่นตัวอย่างเช่นเมื่อแปลงสตริงที่ไม่ได้แสดงถึงตัวเลข


แปลงอย่างไร? ด้วยparseFloat()หรือparseDouble? หรืออย่างอื่น?
Alonso del Arte

14

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


5

NaNหมายถึง "ไม่ใช่ตัวเลข" เป็นค่าทศนิยมพิเศษซึ่งหมายความว่าผลลัพธ์ของการดำเนินการไม่ได้กำหนดไว้หรือไม่สามารถแสดงเป็นจำนวนจริงได้

ดูที่นี่สำหรับคำอธิบายเพิ่มเติมของค่านี้


5

NaN ย่อมาจาก Not a Number ใช้เพื่อแสดงถึงค่าใด ๆ ที่ไม่ได้กำหนดทางคณิตศาสตร์ เช่นหาร 0.0 ด้วย 0.0 คุณสามารถดูข้อมูลเพิ่มเติมได้ที่นี่: https://web.archive.org/web/20120819091816/http://www.concentric.net/~ttwang/tech/javafloat.htm

โพสต์โปรแกรมของคุณที่นี่หากคุณต้องการความช่วยเหลือเพิ่มเติม



4

หมายถึงไม่ใช่ตัวเลข เป็นการแสดงค่าตัวเลขที่เป็นไปไม่ได้ในหลาย ๆ ภาษาโปรแกรม


4

ตัวอย่างที่รันได้น้อยที่สุด

สิ่งแรกที่คุณต้องรู้คือแนวคิดของ NaN นั้นถูกนำไปใช้โดยตรงกับฮาร์ดแวร์ของ CPU

ซีพียูสมัยใหม่ที่สำคัญทั้งหมดดูเหมือนจะเป็นไปตามIEEE 754ซึ่งระบุรูปแบบทศนิยมและ NaN ซึ่งเป็นเพียงค่าลอยพิเศษเป็นส่วนหนึ่งของมาตรฐานนั้น

ดังนั้นแนวคิดจะคล้ายกันมากในทุกภาษารวมถึง Java ซึ่งเพียงแค่ปล่อยรหัสทศนิยมโดยตรงไปยัง CPU

ก่อนดำเนินการต่อคุณอาจต้องอ่านคำตอบต่อไปนี้ที่ฉันเขียนไว้ก่อน:

ตอนนี้สำหรับการดำเนินการบางอย่างของ Java java.lang.Floatส่วนใหญ่ของการทำงานของดอกเบี้ยที่ไม่ได้อยู่ในภาษาหลักภายในสด

Nan.java

import java.lang.Float;
import java.lang.Math;

public class Nan {
    public static void main(String[] args) {
        // Generate some NaNs.
        float nan            = Float.NaN;
        float zero_div_zero  = 0.0f / 0.0f;
        float sqrt_negative  = (float)Math.sqrt(-1.0);
        float log_negative   = (float)Math.log(-1.0);
        float inf_minus_inf  = Float.POSITIVE_INFINITY - Float.POSITIVE_INFINITY;
        float inf_times_zero = Float.POSITIVE_INFINITY * 0.0f;
        float quiet_nan1     = Float.intBitsToFloat(0x7fc00001);
        float quiet_nan2     = Float.intBitsToFloat(0x7fc00002);
        float signaling_nan1 = Float.intBitsToFloat(0x7fa00001);
        float signaling_nan2 = Float.intBitsToFloat(0x7fa00002);
        float nan_minus      = -nan;

        // Generate some infinities.
        float positive_inf   = Float.POSITIVE_INFINITY;
        float negative_inf   = Float.NEGATIVE_INFINITY;
        float one_div_zero   = 1.0f / 0.0f;
        float log_zero       = (float)Math.log(0.0);

        // Double check that they are actually NaNs.
        assert  Float.isNaN(nan);
        assert  Float.isNaN(zero_div_zero);
        assert  Float.isNaN(sqrt_negative);
        assert  Float.isNaN(inf_minus_inf);
        assert  Float.isNaN(inf_times_zero);
        assert  Float.isNaN(quiet_nan1);
        assert  Float.isNaN(quiet_nan2);
        assert  Float.isNaN(signaling_nan1);
        assert  Float.isNaN(signaling_nan2);
        assert  Float.isNaN(nan_minus);
        assert  Float.isNaN(log_negative);

        // Double check that they are infinities.
        assert  Float.isInfinite(positive_inf);
        assert  Float.isInfinite(negative_inf);
        assert !Float.isNaN(positive_inf);
        assert !Float.isNaN(negative_inf);
        assert one_div_zero == positive_inf;
        assert log_zero == negative_inf;
            // Double check infinities.

        // See what they look like.
        System.out.printf("nan            0x%08x %f\n", Float.floatToRawIntBits(nan           ), nan           );
        System.out.printf("zero_div_zero  0x%08x %f\n", Float.floatToRawIntBits(zero_div_zero ), zero_div_zero );
        System.out.printf("sqrt_negative  0x%08x %f\n", Float.floatToRawIntBits(sqrt_negative ), sqrt_negative );
        System.out.printf("log_negative   0x%08x %f\n", Float.floatToRawIntBits(log_negative  ), log_negative  );
        System.out.printf("inf_minus_inf  0x%08x %f\n", Float.floatToRawIntBits(inf_minus_inf ), inf_minus_inf );
        System.out.printf("inf_times_zero 0x%08x %f\n", Float.floatToRawIntBits(inf_times_zero), inf_times_zero);
        System.out.printf("quiet_nan1     0x%08x %f\n", Float.floatToRawIntBits(quiet_nan1    ), quiet_nan1    );
        System.out.printf("quiet_nan2     0x%08x %f\n", Float.floatToRawIntBits(quiet_nan2    ), quiet_nan2    );
        System.out.printf("signaling_nan1 0x%08x %f\n", Float.floatToRawIntBits(signaling_nan1), signaling_nan1);
        System.out.printf("signaling_nan2 0x%08x %f\n", Float.floatToRawIntBits(signaling_nan2), signaling_nan2);
        System.out.printf("nan_minus      0x%08x %f\n", Float.floatToRawIntBits(nan_minus     ), nan_minus     );
        System.out.printf("positive_inf   0x%08x %f\n", Float.floatToRawIntBits(positive_inf  ), positive_inf  );
        System.out.printf("negative_inf   0x%08x %f\n", Float.floatToRawIntBits(negative_inf  ), negative_inf  );
        System.out.printf("one_div_zero   0x%08x %f\n", Float.floatToRawIntBits(one_div_zero  ), one_div_zero  );
        System.out.printf("log_zero       0x%08x %f\n", Float.floatToRawIntBits(log_zero      ), log_zero      );

        // NaN comparisons always fail.
        // Therefore, all tests that we will do afterwards will be just isNaN.
        assert !(1.0f < nan);
        assert !(1.0f == nan);
        assert !(1.0f > nan);
        assert !(nan == nan);

        // NaN propagate through most operations.
        assert Float.isNaN(nan + 1.0f);
        assert Float.isNaN(1.0f + nan);
        assert Float.isNaN(nan + nan);
        assert Float.isNaN(nan / 1.0f);
        assert Float.isNaN(1.0f / nan);
        assert Float.isNaN((float)Math.sqrt((double)nan));
    }
}

GitHub อัปสตรี

ทำงานด้วย:

javac Nan.java && java -ea Nan

เอาท์พุต:

nan            0x7fc00000 NaN
zero_div_zero  0x7fc00000 NaN
sqrt_negative  0xffc00000 NaN
log_negative   0xffc00000 NaN
inf_minus_inf  0x7fc00000 NaN
inf_times_zero 0x7fc00000 NaN
quiet_nan1     0x7fc00001 NaN
quiet_nan2     0x7fc00002 NaN
signaling_nan1 0x7fa00001 NaN
signaling_nan2 0x7fa00002 NaN
nan_minus      0xffc00000 NaN
positive_inf   0x7f800000 Infinity
negative_inf   0xff800000 -Infinity
one_div_zero   0x7f800000 Infinity
log_zero       0xff800000 -Infinity

จากสิ่งนี้เราได้เรียนรู้บางสิ่ง:

  • การดำเนินการลอยตัวแปลก ๆ ที่ไม่มีผลลัพธ์ที่สมเหตุสมผลให้ NaN:

    • 0.0f / 0.0f
    • sqrt(-1.0f)
    • log(-1.0f)

    สร้างไฟล์NaN.

    ในภาษา C เป็นไปได้จริงที่จะขอให้สัญญาณเพิ่มขึ้นในการดำเนินการดังกล่าวfeenableexceptเพื่อตรวจจับ แต่ฉันไม่คิดว่ามันถูกเปิดเผยใน Java: ทำไมการหารจำนวนเต็มด้วยศูนย์ 1/0 จึงให้ข้อผิดพลาด แต่ทศนิยม 1 / 0.0 ส่งคืน "Inf"?

  • การดำเนินการแปลก ๆ ที่อยู่ในขีด จำกัด ของบวกหรือลบอินฟินิตี้อย่างไรก็ตามให้ + - อินฟินิตี้แทน NaN

    • 1.0f / 0.0f
    • log(0.0f)

    0.0 เกือบจะอยู่ในหมวดหมู่นี้ แต่ปัญหาน่าจะเป็นไปได้ว่ามันอาจจะไปที่บวกหรือลบอินฟินิตี้ดังนั้นมันจึงถูกปล่อยให้เป็น NaN

  • หาก NaN เป็นอินพุตของการดำเนินการแบบลอยเอาต์พุตก็มีแนวโน้มที่จะเป็น NaN

  • มีค่าที่เป็นไปได้หลายน่าน0x7fc00000, 0x7fc00001, 0x7fc00002แม้ว่า x86_64 0x7fc00000ดูเหมือนว่าจะสร้างเพียง

  • NaN และ infinity มีการแทนเลขฐานสองที่คล้ายกัน

    มาแบ่งย่อยบางส่วน:

    nan          = 0x7fc00000 = 0 11111111 10000000000000000000000
    positive_inf = 0x7f800000 = 0 11111111 00000000000000000000000
    negative_inf = 0xff800000 = 1 11111111 00000000000000000000000
                                | |        |
                                | |        mantissa
                                | exponent
                                |
                                sign

    จากสิ่งนี้เรายืนยันสิ่งที่ IEEE754 ระบุ:

    • ทั้ง NaN และ infinities มีเลขชี้กำลัง == 255 (ทุกตัว)
    • infinities มี mantissa == 0 ดังนั้นจึงมีเพียงสอง infinities ที่เป็นไปได้: + และ - แตกต่างจากบิตเครื่องหมาย
    • NaN มีแมนทิสซา! = 0 ดังนั้นจึงมีความเป็นไปได้หลายประการยกเว้นแมนทิสซา == 0 ซึ่งเป็นอินฟินิตี้
  • NaN อาจเป็นบวกหรือลบก็ได้ (บิตบนสุด) แม้ว่าจะไม่มีผลต่อการทำงานปกติ

ทดสอบใน Ubuntu 18.10 amd64, OpenJDK 1.8.0_191


3

ไม่ใช่คน Java แต่ใน JS และภาษาอื่น ๆ ฉันใช้ "ไม่ใช่ตัวเลข" ซึ่งหมายความว่าการดำเนินการบางอย่างทำให้มันกลายเป็นตัวเลขที่ไม่ถูกต้อง



3

ไม่ใช่ค่าทศนิยมที่ถูกต้อง (เช่นผลลัพธ์ของการหารด้วยศูนย์)

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


ฉันเล่นลิ้นกับคำตอบนี้ อันดับแรก: "NaN" เป็นค่าที่ถูกต้องสำหรับการลอยตัวของ IEEE! (ท้ายที่สุดมันถูกกำหนดไว้ในข้อมูลจำเพาะ ... ดังนั้นมัน "ถูกต้อง" ใช่มั้ย?) ประการที่สอง: "การหารด้วยศูนย์" สามารถแสดงด้วย IEEE "Positive Infinity" หรือ "Negative Infinity"; ตัวอย่างที่ดีกว่าของ "NaN" คือ "ศูนย์หารด้วยศูนย์" เนื่องจากคำตอบอื่น ๆ ได้ระบุไว้อย่างถูกต้อง
Dan H

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