UFO ET IT

NaN의 비트 패턴이 실제로 하드웨어에 의존합니까?

ufoet 2021. 1. 8. 20:56
반응형

NaN의 비트 패턴이 실제로 하드웨어에 의존합니까?


Java 언어 사양에서 부동 소수점 NaN 값에 대해 읽었습니다 (지루합니다). 32 비트의 float비트 형식은 다음과 같습니다.

seee eeee emmm mmmm mmmm mmmm mmmm mmmm

s부호 비트, e지수 비트 및 m가수 비트입니다. NaN 값은 모두 1의 지수로 인코딩되고 가수 비트는 모두 0이 아닙니다 (+/- 무한대). 이는 가능한 다양한 NaN 값 ( sm비트 값 이 다름)이 많이 있음을 의미합니다 .

이에 대해 JLS §4.2.3 은 다음과 같이 말합니다.

IEEE 754는 단일 및 이중 부동 소수점 형식 각각에 대해 여러 개의 고유 한 NaN 값을 허용합니다. 각 하드웨어 아키텍처는 새 NaN이 생성 될 때 NaN에 대한 특정 비트 패턴을 반환하지만 프로그래머는 예를 들어 후 향적 진단 정보를 인코딩하기 위해 다른 비트 패턴으로 NaN을 생성 할 수도 있습니다.

JLS의 텍스트는 예를 들어의 결과가 0.0/0.0하드웨어 종속적 인 비트 패턴을 가지고 있음을 암시하는 것으로 보이며 해당 표현식이 컴파일 시간 상수로 계산되었는지 여부에 따라 종속되는 하드웨어는 하드웨어가 될 수 있습니다. Java 프로그램이 컴파일되었거나 프로그램이 실행 된 하드웨어입니다. 사실이라면 이 모든 것이 매우 색다른 것처럼 보입니다 .

다음 테스트를 실행했습니다.

System.out.println(Integer.toHexString(Float.floatToRawIntBits(0.0f/0.0f)));
System.out.println(Integer.toHexString(Float.floatToRawIntBits(Float.NaN)));
System.out.println(Long.toHexString(Double.doubleToRawLongBits(0.0d/0.0d)));
System.out.println(Long.toHexString(Double.doubleToRawLongBits(Double.NaN)));

내 컴퓨터의 출력은 다음과 같습니다.

7fc00000
7fc00000
7ff8000000000000
7ff8000000000000

출력은 예상과 다른 것을 보여줍니다. 지수 비트는 모두 1입니다. 가수의 위쪽 비트도 1입니다. NaN의 경우 "신호를 보내는 NaN"( https://en.wikipedia.org/wiki/NaN#)이 아니라 "조용한 NaN"을 나타냅니다. 부동 소수점 ). 부호 비트와 나머지 가수 비트는 0입니다. 출력은 또한 내 컴퓨터에서 생성 된 NaN과 Float 및 Double 클래스의 상수 NaN간에 차이가 없음을 보여줍니다.

내 질문은 컴파일러 또는 VM의 CPU에 관계없이 Java에서 출력이 보장 됩니까, 아니면 모두 진정으로 예측할 수 없습니까? JLS는 이것에 대해 신비합니다.

해당 출력이에 대해 보장되는 0.0/0.0경우 다른 (아마도 하드웨어 종속적 인?) 비트 패턴이있는 NaN을 생성하는 산술적 방법이 있습니까? ( 다른 NaN을 intBitsToFloat/ longBitsToDouble인코딩 할 수 있다는 것을 알고 있지만 일반 산술에서 다른 값이 발생할 수 있는지 알고 싶습니다.)


후속 요점 : Float.NaNDouble.NaN 이 정확한 비트 패턴을 지정하지만 소스 ( Float , Double )에서는 0.0/0.0. 그 분할의 결과가 실제로 컴파일러의 하드웨어에 의존한다면 사양이나 구현에 결함이있는 것처럼 보입니다.


이것은 JVM 7 사양의 §2.3.2가 그것에 대해 말하는 것입니다.

이중 값 집합의 요소는 NaN 값이 하나만 있다는 점을 제외하고 IEEE 754 표준에 정의 된 이중 부동 소수점 형식을 사용하여 표현할 수있는 값입니다 (IEEE 754는 2 53 -2 고유 NaN 값을 지정 함).

§2.8.1 :

Java Virtual Machine에는 신호 NaN 값이 없습니다.

기술적으로 NaN은 하나뿐입니다. 그러나 JLS의 §4.2.3은 또한 다음과 같이 말합니다.

대부분의 경우 Java SE 플랫폼은 주어진 유형의 NaN 값을 단일 표준 값으로 축소 된 것처럼 취급하므로이 사양은 일반적으로 표준 값처럼 임의의 NaN을 참조합니다.

그러나 Java SE 플랫폼의 버전 1.3에는 프로그래머가 NaN 값을 구별 할 수있는 Float.floatToRawIntBits 및 Double.doubleToRawLongBits 메소드가 도입되었습니다. 관심있는 독자는 자세한 정보를 위해 Float 및 Double 클래스의 사양을 참조하십시오.

이것이 바로 당신과 CandiedOrange가 제안 하는 것을 의미 합니다. 기본 프로세서에 따라 다르지만 Java는 모두 동일하게 취급합니다.

하지만 더 나아집니다. 다음에서 설명하는 것처럼 NaN 값이 다른 NaN으로 자동 변환되는 것이 전적으로 가능합니다 Double.longBitsToDouble().

이 메서드는 long 인수와 정확히 동일한 비트 패턴으로 이중 NaN을 반환하지 못할 수 있습니다. IEEE 754는 두 종류의 NaN, 조용한 NaN 및 신호 NaN을 구분합니다. 두 종류의 NaN 간의 차이점은 일반적으로 Java에서 볼 수 없습니다. 신호 NaN에 대한 산술 연산은 그것들을 다르지만 종종 유사한 비트 패턴을 가진 조용한 NaN으로 바꿉니다. 그러나 일부 프로세서에서는 단순히 신호 NaN을 복사하는 것만으로도 해당 변환을 수행합니다. 특히 시그널링 NaN을 복사하여 호출 메서드로 반환하면이 변환이 수행 될 수 있습니다. 따라서 longBitsToDouble은 신호 NaN 비트 패턴으로 double을 반환하지 못할 수 있습니다. 따라서 일부 long 값의 경우 doubleToRawLongBits (longBitsToDouble (start))가 시작과 같지 않을 수 있습니다. 게다가, 어떤 특정 비트 패턴이 신호 NaN을 나타내는 지 플랫폼에 따라 다릅니다. 모든 NaN 비트 패턴 (무음 또는 신호)은 위에서 식별 된 NaN 범위에 있어야합니다.

참고로 여기에 하드웨어 종속 NaN 표가 있습니다 . 요약하자면:

- x86:     
   quiet:      Sign=0  Exp=0x7ff  Frac=0x80000
   signalling: Sign=0  Exp=0x7ff  Frac=0x40000
- PA-RISC:               
   quiet:      Sign=0  Exp=0x7ff  Frac=0x40000
   signalling: Sign=0  Exp=0x7ff  Frac=0x80000
- Power:
   quiet:      Sign=0  Exp=0x7ff  Frac=0x80000
   signalling: Sign=0  Exp=0x7ff  Frac=0x5555555500055555
- Alpha:
   quiet:      Sign=0  Exp=0      Frac=0xfff8000000000000
   signalling: Sign=1  Exp=0x2aa  Frac=0x7ff5555500055555

따라서 이것을 확인하려면 이러한 프로세서 중 하나가 실제로 필요하고 시도해보십시오. 또한 Power 및 Alpha 아키텍처의 더 긴 값을 해석하는 방법에 대한 통찰력도 환영합니다.


다음은 다양한 NaN 비트 패턴을 보여주는 프로그램입니다.

public class Test {
  public static void main(String[] arg) {
    double myNaN = Double.longBitsToDouble(0x7ff1234512345678L);
    System.out.println(Double.isNaN(myNaN));
    System.out.println(Long.toHexString(Double.doubleToRawLongBits(myNaN)));
    final double zeroDivNaN = 0.0 / 0.0;
    System.out.println(Double.isNaN(zeroDivNaN));
    System.out
        .println(Long.toHexString(Double.doubleToRawLongBits(zeroDivNaN)));
  }
}

산출:

true
7ff1234512345678
true
7ff8000000000000

하드웨어가 수행하는 작업에 관계없이 프로그램은 예와 같지 않을 수있는 NaN을 생성 0.0/0.0할 수 있으며 프로그램에서 어떤 의미를 가질 수 있습니다.


여기서 JLS를 읽는 방식은 NaN의 정확한 비트 값은 누가 / 무엇을 만들 었는지에 따라 다르며 JVM이 만들지 않았으므로 묻지 마십시오. "오류 코드 4"문자열이 무엇을 의미하는지 물어 보는 것이 좋습니다.

The hardware produces different bit patterns meant to represent different kinds of NaN's. Unfortunately the different kinds hardware produce different bit patterns for the same kinds of NaN's. Fortunately there is a standard pattern that Java can use to at least tell that it is some kind of NaN.

It's like Java looked at the "Error code 4" string and said, "We don't know what 'code 4' means on your hardware, but there was the word 'error' in that string, so we think it's an error."

The JLS tries to give you a chance to figure it out on your own though:

"However, version 1.3 of the Java SE platform introduced methods enabling the programmer to distinguish between NaN values: the Float.floatToRawIntBits and Double.doubleToRawLongBits methods. The interested reader is referred to the specifications for the Float and Double classes for more information."

Which looks to me like a C++ reinterpret_cast. It's Java giving you a chance to analyze the NaN yourself in case you happen to know how its signal was encoded. If you want to track down the hardware specs so you can predict what different events should produce which NaN bit patterns you are free to do so but you are outside the uniformity the JVM was meant to give us. So expect it might change from hardware to hardware.

When testing if a number is NaN we check if it's equal to itself since it's the only number that isn't. This isn't to say that the bits are different. Before comparing the bits the JVM tests for the many bit patterns that say it's a NaN. If it's any of those patterns then it reports that it's not equal, even if the bits of the two operands really are the same (and even if they're different).

Back in 1964, when pressed to give an exact definition for pornography, U.S. Supreme Court Justice Stewart famously said, “I Know It When I See It”. I think of Java as doing the same thing with NaN's. Java can't tell you anything that a "signaling" NaN might be signaling cause it doesn't know how that signal was encoded. But it can look at the bits and tell it's a NaN of some kind since that pattern follows one standard.

If you happen to be on hardware that encodes all NaN's with uniform bits you'll never prove that Java is doing anything to make NaN's have uniform bits. Again, the way I read the JLS, they are outright saying you are on your own here.

I can see why this feels flaky. It is flaky. It's just not Java's fault. I'll lay odds that some where out there some enterprising hardware manufactures came up with wonderfully expressive signaling NaN bit patterns but they failed to get it adopted as a standard widely enough that Java can count on it. That's what's flaky. We have all these bits reserved for signalling what kind of NaN we have and can't use them because we don't agree what they mean. Having Java beat NaN's into a uniform value after the hardware makes them would only destroy that information, harm performance, and the only payoff is to not seem flaky. Given the situation, I'm glad they realized they could cheat their way out of the problem and define NaN as not being equal to anything.


The only other NaN value that I could generate with normal arithmetic operations so far is the same but with the sign changed:

public static void main(String []args) {
    Double tentative1 = 0d/0d;
    Double tentative2 = Math.sqrt(-1d);

    System.out.println(tentative1);
    System.out.println(tentative2);

    System.out.println(Long.toHexString(Double.doubleToRawLongBits(tentative1)));
    System.out.println(Long.toHexString(Double.doubleToRawLongBits(tentative2)));

    System.out.println(tentative1 == tentative2);
    System.out.println(tentative1.equals(tentative2));
}

Output:

NaN

NaN

7ff8000000000000

fff8000000000000

false

true

ReferenceURL : https://stackoverflow.com/questions/25050133/are-the-bit-patterns-of-nans-really-hardware-dependent

반응형