Java에서 NaN은 무엇을 의미합니까?


107

double원하는 수로 줄이려고하는 프로그램이 있습니다. 내가 얻는 출력은 NaN.

NaNJava에서 무엇을 의미합니까?


NaN에 대한 좋은 설명과 Java에서 NaN을 사용할 때 일반적인 함정에 대한 설명이 있습니다. ppkwok.blogspot.co.uk/2012/11/…
Phil

"NaN이 무슨 소용이 있습니까?"라고 묻는다면 Java (또는 다른 언어)에서 매우 편리한 사용 사례를 제공 할 수 있습니다. 2 차원 부동 소수점 배열이 있지만 계산에 해당 2 차원 배열의 일부에 대해 의미있는 값이없는 경우 이 값을 "NaN"으로 채울 것입니다. 이것은 내 계산의 하위 사용자 (예 : 래스터 이미지로 바뀔 때)에 "이 시점에서 값에주의를 기울이지 마십시오"를 알리는 데 사용할 수 있습니다. 굉장히 유용하다!
Dan H

BTW, 정확히 더블을 "축소"한다는 것은 무엇을 의미합니까? 호기심 ...
댄 H

답변:


153

이 페이지 에서 가져온 :

"NaN"은 "숫자가 아님"을 의미합니다. "Nan"은 부동 소수점 연산에 정의되지 않은 결과를 생성하는 입력 매개 변수가있는 경우 생성됩니다. 예를 들어 0.0을 0.0으로 나눈 값은 산술적으로 정의되지 않습니다. 음수의 제곱근을 취하는 것도 정의되지 않습니다.


16
또한 NaN은 Java가 맹목적으로 따르는 부동 소수점 산술을위한 IEEE 표준 (IEEE 754)에 의해 매우 명시 적으로 정의됩니다. 표준을 읽는 것은 많은 것에 눈을 뜨게하는데, 0의 다중 값이 그 중 하나입니다.
Esko

37
또한 NaN비교할 때 자신과 동일하지 않은 유일한 "숫자"라는 흥미로운 속성이 있습니다. 따라서 숫자 xNaN다음과 같은 경우 일반적인 (그리고 여러 언어에서 유일한) 테스트 :boolean isNaN(x){return x != x;}
quazgar

3
대답의 링크가 죽었습니까?
Pang

3
... "음수의 제곱근을 취하는 것은 (산술에서) 정의되지 않았습니다."... 그게 아닙니다! 그것의 사실 i과 파이썬과 같은 일부 언어는 그것을 아주 잘 처리합니다 ... 그것은 java당신 의 경우가 아닐 수도 있습니다
Rafael T

5
@RafaelT 복잡하지 않은 산술에서는 정의되지 않았다고 말하고 싶습니다. Java에서 float 또는 double에 복소수를 할당하는 방법은 없습니다. Python은 동적으로 형식화되므로이 경우 복소수 만 반환 할 수 있습니다.
sstn

19

NaN"숫자가 아님"을 의미하며 기본적으로 IEE 754 부동 소수점 표준 의 특수 부동 소수점 값을 나타냅니다 . NaN 일반적으로 값이 유효한 부동 소수점 숫자로 표현 될 수없는 것을 의미합니다.

예를 들어 숫자를 나타내지 않는 문자열을 변환하는 경우와 같이 변환되는 값이 다른 경우 변환은이 값을 생성합니다.


어떻게 변환합니까? 와 parseFloat()parseDouble? 또는 다른 것?
Alonso del Arte

14

NaN"숫자가 아님"을 의미하며 부동 소수점 숫자에 대한 정의되지 않은 연산의 결과입니다 (예 : 0을 0으로 나누기). (0이 아닌 숫자를 0으로 나누는 것도 일반적으로 수학에서 정의되지 않지만 NaN이 아닌 양 또는 음의 무한대가됩니다).


5

NaN"숫자가 아님"을 의미합니다. 연산의 결과가 정의되지 않았거나 실수로 표현할 수 없음을 의미하는 특별한 부동 소수점 값입니다.

이 값에 대한 자세한 설명은 여기 를 참조 하십시오 .




4

숫자가 아님을 의미합니다. 많은 프로그래밍 언어에서 불가능한 숫자 값에 대한 일반적인 표현입니다.


4

최소 실행 가능 예

가장 먼저 알아야 할 것은 NaN의 개념이 CPU 하드웨어에서 직접 구현된다는 것입니다.

모든 주요 최신 CPU는 부동 소수점 형식을 지정하는 IEEE 754 를 따르는 것으로 보이며 특수 부동 값인 NaN은 해당 표준의 일부입니다.

따라서 개념은 CPU에 직접 부동 소수점 코드를 내보내는 Java를 포함하여 모든 언어에서 매우 유사합니다.

계속하기 전에 먼저 내가 작성한 다음 답변을 읽어 보는 것이 좋습니다.

이제 몇 가지 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에서는 노출되지 않는다고 생각합니다. 0으로 정수 나누기 1/0으로 오류가 발생하지만 부동 소수점 1 / 0.0이 발생하는 이유는 무엇입니까? "Inf"를 반환합니까?

  • 플러스 또는 마이너스 무한대의 한계에있는 이상한 연산은 NaN 대신 +-무한대를 제공합니다.

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

    0.0 거의이 범주에 속하지만 문제는 플러스 또는 마이너스 무한대로 갈 수 있다는 것이므로 NaN으로 남았습니다.

  • NaN이 부동 연산의 입력이면 출력도 NaN이되는 경향이 있습니다.

  • 몇 가지 가능한 값이 NaN 거기에있다 0x7fc00000, 0x7fc00001, 0x7fc00002, x86_64의 경우에만 생성 보이더라도 0x7fc00000.

  • NaN과 무한대는 이진 표현이 비슷합니다.

    그중 몇 가지를 분석해 보겠습니다.

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

    이것으로부터 우리는 IEEE754가 지정하는 것을 확인합니다 :

    • NaN과 무한대 모두 지수 == 255 (모두 1)
    • 무한대에는 가수 == 0이 있습니다. 따라서 가능한 무한대는 + 및-, 부호 비트로 구분됩니다.
    • NaN에는 가수! = 0이 있습니다. 따라서 무한대 인 가수 == 0을 제외하고 여러 가능성이 있습니다.
  • NaN은 양수 또는 음수 (상위 비트) 일 수 있지만 정상적인 작업에는 영향을주지 않습니다.

Ubuntu 18.10 amd64, OpenJDK 1.8.0_191에서 테스트되었습니다.


3

Java 사람은 아니지만 JS 및 기타 언어에서는 "Not a Number"를 사용합니다. 즉, 일부 작업으로 인해 유효한 숫자가 아닙니다.


3

문자 그대로 "숫자가 아님"을 의미합니다. 전환 과정에 문제가있는 것 같습니다.

이 참조 에서 Not A Number 섹션을 확인하십시오.


3

유효한 부동 소수점 값이 아닙니다 (예 : 0으로 나눈 결과).

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


나는이 대답으로 고민한다. 첫째 : "NaN"은 IEEE float에 유효한 값입니다! (결국 사양에 정의되어 있으므로 "유효"합니다.) 둘째 : "0으로 나누기"는 IEEE "Positive Infinity"또는 "Negative Infinity"로 나타낼 수 있습니다. "NaN"의 더 나은 예는 다른 답변이 올바르게 지적했듯이 "0을 0으로 나누기"입니다.
Dan H

"유효한 값"과 "사양에 정의 됨"은 동일하지 않습니다. 0/0에 동의했습니다.
Vladimir Dyuzhev
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.