Trong Java, NaN có nghĩa là gì?


107

Tôi có một chương trình cố gắng thu nhỏ doublexuống một con số mong muốn. Đầu ra tôi nhận được là NaN.

Không gì NaNcó nghĩa là trong Java?


Có một mô tả tốt về NaN và các cạm bẫy phổ biến khi sử dụng NaN trong Java: ppkwok.blogspot.co.uk/2012/11/…
Phil

Nếu bạn đang hỏi "NaN có gì tốt?" trong Java (hoặc bất kỳ ngôn ngữ nào khác), tôi có thể cung cấp cho bạn một trường hợp sử dụng rất hữu ích: khi tôi có mảng float 2-D, nhưng phép tính của tôi không có giá trị có ý nghĩa đối với một số phần của mảng 2-D đó, Tôi sẽ điền giá trị đó bằng "NaN". Điều này có thể được sử dụng để báo hiệu cho những người dùng xuôi dòng về phép tính của tôi (chẳng hạn như khi nó được chuyển thành hình ảnh raster) "không chú ý đến giá trị tại thời điểm này". Rất hữu ích!
Dan H

BTW, chính xác - nó có nghĩa là gì để "thu nhỏ" một đôi? Tò mò ...
Dan H

Câu trả lời:


153

Lấy từ trang này :

"NaN" là viết tắt của "not a number". "Nan" được tạo ra nếu một phép toán dấu chấm động có một số tham số đầu vào khiến hoạt động tạo ra một số kết quả không xác định. Ví dụ, 0,0 chia cho 0,0 là không xác định về mặt số học. Lấy căn bậc hai của một số âm cũng không xác định.


16
Ngoài ra, NaN được định nghĩa bởi Tiêu chuẩn IEEE cho Số học Dấu phẩy động (IEEE 754) khá rõ ràng mà Java tuân theo một cách mù quáng. Đọc tiêu chuẩn sẽ mở ra cho bạn rất nhiều thứ, nhiều giá trị của 0 là một trong những thứ.
Esko

37
Ngoài ra, NaNcó tính chất thú vị là "số" duy nhất không giống với chính nó khi so sánh. Do đó, một thử nghiệm phổ biến (và trong nhiều ngôn ngữ là duy nhất) nếu một số xNaNnhư sau:boolean isNaN(x){return x != x;}
quazgar

3
Liên kết trong câu trả lời đã chết?
Pang

3
... "Lấy căn bậc hai của số âm là không xác định (trong số học)" ... Không phải! nó thực sự ivà một số ngôn ngữ như python đối phó rất tốt với nó ... Nó có thể không phải là trường hợp của javabạn
Rafael T

5
@RafaelT Tôi muốn nói rằng nó không được xác định trong số học không phức tạp. Không có cách nào để gán một số phức cho float hoặc double trong Java. Python được nhập động, do đó có thể chỉ trả về một số phức trong trường hợp này.
sstn

19

NaNcó nghĩa là “Không phải số” và về cơ bản là đại diện của một giá trị dấu phẩy động đặc biệt trong tiêu chuẩn dấu chấm động IEE 754 . NaN thường có nghĩa là giá trị là thứ không thể được biểu thị bằng một số dấu phẩy động hợp lệ.

Một chuyển đổi sẽ dẫn đến giá trị này, khi giá trị được chuyển đổi là giá trị khác, ví dụ: khi chuyển đổi một chuỗi không đại diện cho một số.


Chuyển đổi như thế nào? Với parseFloat()hoặc parseDouble? Hay cái gì khác?
Alonso del Arte

14

NaNcó nghĩa là "Không phải là Số" và là kết quả của các phép toán không xác định trên các số dấu phẩy động, chẳng hạn như chia 0 cho 0. (Lưu ý rằng trong khi phép chia một số khác 0 cho không cũng thường không được xác định trong toán học, nó không dẫn đến NaN mà là dương hoặc âm vô cùng).


5

NaNcó nghĩa là "Không phải số". Đó là một giá trị dấu phẩy động đặc biệt có nghĩa là kết quả của một phép toán không được xác định hoặc không thể biểu diễn dưới dạng số thực.

Xem ở đây để giải thích thêm về giá trị này.




4

Có nghĩa không phải là một con số. Nó là một đại diện phổ biến cho một giá trị số không thể có trong nhiều ngôn ngữ lập trình.


4

Ví dụ tối thiểu có thể chạy được

Điều đầu tiên bạn phải biết, đó là khái niệm NaN được thực hiện trực tiếp trên phần cứng CPU.

Tất cả các CPU hiện đại chính dường như tuân theo IEEE 754 chỉ định các định dạng dấu chấm động và NaN, chỉ là các giá trị float đặc biệt, là một phần của tiêu chuẩn đó.

Do đó, khái niệm này sẽ rất giống nhau trên bất kỳ ngôn ngữ nào, bao gồm cả Java chỉ phát ra mã dấu phẩy động trực tiếp đến CPU.

Trước khi tiếp tục, trước tiên bạn có thể muốn đọc các câu trả lời tôi đã viết sau đây:

Bây giờ cho một số hành động Java. Hầu hết các chức năng quan tâm không có trong ngôn ngữ cốt lõi đều nằm bên trong 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 ngược dòng .

Chạy với:

javac Nan.java && java -ea Nan

Đầu ra:

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

Vì vậy, từ điều này, chúng tôi học được một số điều:

  • Các phép toán trôi nổi kỳ lạ không có bất kỳ kết quả hợp lý nào cho NaN:

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

    tạo ra một NaN.

    Trong C, thực sự có thể yêu cầu các tín hiệu được nâng lên trên các hoạt động như vậy feenableexceptđể phát hiện chúng, nhưng tôi không nghĩ rằng nó được hiển thị trong Java: Tại sao phép chia số nguyên cho 0 1/0 lại cho lỗi nhưng dấu phẩy động 1 / 0,0 trả về "Inf"?

  • các phép toán kỳ lạ nằm trong giới hạn của cộng hoặc trừ vô cùng tuy nhiên lại cho + - vô cực thay vì NaN

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

    0.0 gần như thuộc loại này, nhưng có thể vấn đề là nó có thể đi đến cộng hoặc trừ vô cùng, vì vậy nó được để là NaN.

  • nếu NaN là đầu vào của một hoạt động thả nổi, thì đầu ra cũng có xu hướng là NaN

  • có một số giá trị có thể cho NaN 0x7fc00000, 0x7fc00001, 0x7fc00002, mặc dù x86_64 dường như chỉ tạo ra 0x7fc00000.

  • NaN và vô cùng có biểu diễn nhị phân tương tự.

    Hãy chia nhỏ một vài trong số chúng:

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

    Từ đó, chúng tôi xác nhận những gì IEEE754 chỉ định:

    • cả NaN và số nguyên đều có số mũ == 255 (tất cả các số đều)
    • các số nguyên có định trị == 0. Do đó chỉ có hai số nguyên có thể có: + và -, được phân biệt bằng bit dấu
    • NaN có phần định trị! = 0. Do đó có một số khả năng xảy ra, ngoại trừ phần định trị == 0 là vô cùng
  • NaN có thể là tích cực hoặc tiêu cực (bit trên cùng), mặc dù nó không ảnh hưởng đến hoạt động bình thường

Đã thử nghiệm trong Ubuntu 18.10 amd64, OpenJDK 1.8.0_191.


3

Không phải là một người chơi Java, nhưng trong JS và các ngôn ngữ khác, tôi sử dụng nó là "Not a Number", có nghĩa là một số hoạt động đã khiến nó trở thành một số không hợp lệ.



3

Không phải là giá trị dấu phẩy động hợp lệ (ví dụ: kết quả của phép chia cho 0)

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


Tôi phân minh với câu trả lời này. Đầu tiên: "NaN" LÀ một giá trị hợp lệ cho một IEEE float! (Sau khi tất cả, nó được định nghĩa trong spec ... vì vậy "hợp lệ" của nó, phải không?). Thứ hai: "phép chia cho không" có thể được biểu diễn bằng "Vô cực dương" hoặc "Vô cực âm" của IEEE; một ví dụ tốt hơn về "NaN" là "số không chia cho số không", như một số câu trả lời khác đã chỉ ra chính xác.
Dan H

"Giá trị hợp lệ" và "được xác định trong đặc điểm kỹ thuật" không giống nhau. Đồng ý với 0/0.
Vladimir Dyuzhev
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.