問題描述
我正在閱讀 Java 語言規(guī)范中的浮點(diǎn) NaN 值(我很無聊).32 位 float
具有這種位格式:
I was reading about floating-point NaN values in the Java Language Specification (I'm boring). A 32-bit float
has this bit format:
seee eeee emmm mmmm mmmm mmmm mmmm mmmm
s
是符號位,e
是指數(shù)位,m
是尾數(shù)位.NaN 值被編碼為全 1 的指數(shù),并且尾數(shù)位不全為 0(這將是 +/- 無窮大).這意味著有許多不同的可能 NaN 值(具有不同的 s
和 m
位值).
s
is the sign bit, e
are the exponent bits, and m
are the mantissa bits. A NaN value is encoded as an exponent of all 1s, and the mantissa bits are not all 0 (which would be +/- infinity). This means that there are lots of different possible NaN values (having different s
and m
bit values).
對此,JLS§4.2.3 說:
IEEE 754 允許其單雙浮點(diǎn)格式中的每一種都有多個不同的 NaN 值.雖然每個硬件架構(gòu)在生成新的 NaN 時都會返回特定的 NaN 位模式,但程序員也可以創(chuàng)建具有不同位模式的 NaN 來編碼,例如追溯診斷信息.
IEEE 754 allows multiple distinct NaN values for each of its single and double floating-point formats. While each hardware architecture returns a particular bit pattern for NaN when a new NaN is generated, a programmer can also create NaNs with different bit patterns to encode, for example, retrospective diagnostic information.
JLS 中的文本似乎暗示,例如,0.0/0.0
的結(jié)果具有與硬件相關(guān)的位模式,并且取決于該表達(dá)式是否被計(jì)算為編譯時間常量,它所依賴的硬件可能是編譯 Java 程序的硬件或運(yùn)行程序的硬件.如果屬實(shí),這一切似乎非常不穩(wěn)定.
The text in the JLS seems to imply that the result of, for example, 0.0/0.0
, has a hardware-dependent bit pattern, and depending on whether that expression was computed as a compile time constant, the hardware it is dependent on might be the hardware the Java program was compiled on or the hardware the program was run on. This all seems very flaky if true.
我進(jìn)行了以下測試:
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)));
我機(jī)器上的輸出是:
7fc00000
7fc00000
7ff8000000000000
7ff8000000000000
輸出沒有顯示任何超出預(yù)期的內(nèi)容.指數(shù)位都是 1.尾數(shù)的高位也是 1,這對于 NaN 顯然表示安靜的 NaN"而不是發(fā)信號的 NaN"(https://en.wikipedia.org/wiki/NaN#Floating_point).符號位和尾數(shù)位的其余部分為 0.輸出還顯示,在我的機(jī)器上生成的 NaN 與 Float 和 Double 類中的常量 NaN 沒有區(qū)別.
The output shows nothing out of the expected. The exponent bits are all 1. The upper bit of the mantissa is also 1, which for NaNs apparently indicates a "quiet NaN" as opposed to a "signalling NaN" (https://en.wikipedia.org/wiki/NaN#Floating_point). The sign bit and the rest of the mantissa bits are 0. The output also shows that there was no difference between the NaNs generated on my machine and the constant NaNs from the Float and Double classes.
我的問題是,無論編譯器或虛擬機(jī)的 CPU 是多少,Java 是否都能保證輸出,還是真的無法預(yù)測?JLS 對此很神秘.
My question is, is that output guaranteed in Java, regardless of the CPU of the compiler or VM, or is it all genuinely unpredictable? The JLS is mysterious about this.
如果 0.0/0.0
保證該輸出,是否有任何算術(shù)方法可以生成具有其他(可能與硬件相關(guān)?)位模式的 NaN?(我知道 intBitsToFloat
/longBitsToDouble
可以編碼其他 NaN,但我想知道其他值是否可以從正常算術(shù)中產(chǎn)生.)
If that output is guaranteed for 0.0/0.0
, are there any arithmetic ways of producing NaNs that do have other (possibly hardware-dependent?) bit patterns? (I know intBitsToFloat
/longBitsToDouble
can encode other NaNs, but I'd like to know if other values can occur from normal arithmetic.)
后續(xù)要點(diǎn):我注意到 Float.NaN 和 雙倍.NaN 指定它們的確切位模式,但在源 (浮動, Double) 它們由 0.0/0.0
生成.如果該劃分的結(jié)果確實(shí)取決于編譯器的硬件,那么無論是規(guī)范還是實(shí)現(xiàn)似乎都存在缺陷.
A followup point: I've noticed that Float.NaN and Double.NaN specify their exact bit pattern, but in the source (Float, Double) they are generated by 0.0/0.0
. If the result of that division is really dependent on the hardware of the compiler, it seems like there is a flaw there in either the spec or the implementation.
推薦答案
這就是 §2.3.2 of the JVM 7 spec 不得不說:
雙值集合的元素正是可以表示的值使用 IEEE 754 標(biāo)準(zhǔn)中定義的雙浮點(diǎn)格式,除了只有一個 NaN 值(IEEE 754 指定 253-2 個不同的 NaN 值).
The elements of the double value set are exactly the values that can be represented using the double floating-point format defined in the IEEE 754 standard, except that there is only one NaN value (IEEE 754 specifies 253-2 distinct NaN values).
和 §2.8.1:
Java 虛擬機(jī)沒有信號 NaN 值.
The Java Virtual Machine has no signaling NaN value.
所以從技術(shù)上講,只有一個 NaN.但是 §4.2.3JLS 還說(在您的報價之后):
So technically there is only one NaN. But §4.2.3 of the JLS also says (right after your quote):
在大多數(shù)情況下,Java SE 平臺將給定類型的 NaN 值視為折疊為單個規(guī)范值,因此本規(guī)范通常將任意 NaN 稱為規(guī)范值.
For the most part, the Java SE platform treats NaN values of a given type as though collapsed into a single canonical value, and hence this specification normally refers to an arbitrary NaN as though to a canonical value.
但是,Java SE 平臺 1.3 版引入了使程序員能夠區(qū)分 NaN 值的方法:Float.floatToRawIntBits 和 Double.doubleToRawLongBits 方法.感興趣的讀者可以參考 Float 和 Double 類的規(guī)范以獲取更多信息.
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.
我認(rèn)為這正是您和 CandiedOrange 建議的意思:它依賴于底層處理器,但 Java 對待它們都一樣.
Which I take to mean exactly what you and CandiedOrange propose: It is dependent on the underlying processor, but Java treats them all the same.
但它變得更好:顯然,您的 NaN 值完全有可能被靜默轉(zhuǎn)換為不同的 NaN,如 Double.longBitsToDouble()
:
But it gets better: Apparently, it is entirely possible that your NaN values are silently converted to different NaNs, as described in Double.longBitsToDouble()
:
請注意,此方法可能無法返回具有與 long 參數(shù)完全相同的位模式的雙 NaN.IEEE 754 區(qū)分了兩種 NaN,靜默 NaN 和信令 NaN.這兩種 NaN 之間的差異在 Java 中通常是不可見的.信號 NaN 的算術(shù)運(yùn)算將它們變成安靜的 NaN,具有不同但通常相似的位模式.但是,在某些處理器上,僅復(fù)制信號 NaN 也會執(zhí)行該轉(zhuǎn)換.特別是,復(fù)制一個信令 NaN 以將其返回給調(diào)用方法可以執(zhí)行此轉(zhuǎn)換.因此 longBitsToDouble 可能無法返回帶有信號 NaN 位模式的雙精度數(shù).因此,對于某些 long 值,doubleToRawLongBits(longBitsToDouble(start)) 可能不等于 start.此外,哪些特定的位模式代表信令 NaN 取決于平臺;盡管所有 NaN 位模式,安靜或信令,都必須在上面確定的 NaN 范圍內(nèi).
Note that this method may not be able to return a double NaN with exactly same bit pattern as the long argument. IEEE 754 distinguishes between two kinds of NaNs, quiet NaNs and signaling NaNs. The differences between the two kinds of NaN are generally not visible in Java. Arithmetic operations on signaling NaNs turn them into quiet NaNs with a different, but often similar, bit pattern. However, on some processors merely copying a signaling NaN also performs that conversion. In particular, copying a signaling NaN to return it to the calling method may perform this conversion. So longBitsToDouble may not be able to return a double with a signaling NaN bit pattern. Consequently, for some long values, doubleToRawLongBits(longBitsToDouble(start)) may not equal start. Moreover, which particular bit patterns represent signaling NaNs is platform dependent; although all NaN bit patterns, quiet or signaling, must be in the NaN range identified above.
作為參考,這里有一個與硬件相關(guān)的 NaN 表.總結(jié):
For reference, there is a table of the hardware-dependant NaNs here. In summary:
- 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
因此,要驗(yàn)證這一點(diǎn),您確實(shí)需要這些處理器之一并嘗試一下.此外,歡迎任何關(guān)于如何解釋 Power 和 Alpha 架構(gòu)的較長值的見解.
So, to verify this you would really need one of these processors and go try it out. Also any insights on how to interpret the longer values for the Power and Alpha architectures are welcome.
這篇關(guān)于NaN 的位模式真的依賴于硬件嗎?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!