admin管理员组

文章数量:1355278

In JavaScript, the NaN value can be represented by a wide range of 64-bit doubles internally. Specifically, any double with the following bitwise representation:

x111 1111 1111 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx

Is interpreted as a NaN. My question is: suppose I cast two 32-bit uints to a JS Number using ArrayBuffers, pass it around, then cast it back to two 32-bit uints. Will the recovered bits be the same as the original, or are JS engines allowed to change the bits of a NaN at will? In other words, can JS numbers be used to store 64-bits losslesly?

In JavaScript, the NaN value can be represented by a wide range of 64-bit doubles internally. Specifically, any double with the following bitwise representation:

x111 1111 1111 xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx

Is interpreted as a NaN. My question is: suppose I cast two 32-bit uints to a JS Number using ArrayBuffers, pass it around, then cast it back to two 32-bit uints. Will the recovered bits be the same as the original, or are JS engines allowed to change the bits of a NaN at will? In other words, can JS numbers be used to store 64-bits losslesly?

Share Improve this question asked Nov 7, 2019 at 20:55 MaiaVictorMaiaVictor 53.1k47 gold badges158 silver badges302 bronze badges 3
  • 2 Interesting idea – Evert Commented Nov 7, 2019 at 20:57
  • 1 A test I made. Seems like at least Node.js changes the bits at will, causing loss of information. – MaiaVictor Commented Nov 7, 2019 at 21:53
  • 1 Aside: Not every such bit pattern represents a NaN. If all x buts after the first are zero, it represents an infinity. – Eric Postpischil Commented Nov 8, 2019 at 8:23
Add a ment  | 

4 Answers 4

Reset to default 6

ECMA-262 9th Edition, June 2018, (the standard to which JavaScript is intended to conform) says, in 6.1.6 “The Number Type”:

… the 9007199254740990 (that is, 253-2) distinct “Not-a-Number” values of the IEEE Standard are represented in ECMAScript as a single special NaN value.… In some implementations, external code might be able to detect a difference between various Not-a-Number values, but such behaviour is implementation-dependent; to ECMAScript code, all NaN values are indistinguishable from each other.

24.1.17 “NumberToRawBytes ( type, value, isLittleEndian )” says:

… If value is NaN, rawBytes may be set to any implementation chosen IEEE 754-2008 binary64 format Not-a-Number encoding. An implementation must always choose the same encoding for each implementation distinguishable NaN value.…

I do not see any other passages that mention NaN that are illuminating on this question. On one hand, 24.1.17 effectively tells us the bits of a NaN must be preserved when converting the NaN to raw bytes. However, nothing else appears to tell us the bits must be preserved in other operations. One might deduce that this is the intent, because this requirement in 24.1.17 would serve no purpose if the bits could be arbitrarily changed by any other operation. But I would not rely on JavaScript implementations to have implemented this in conformance with that intent.

I once asked a question for Java, about hardware-dependence of NaN values, and it was noticed that some CPUs will silently convert a "signaling NaN" into a "quiet NaN" (setting the quiet NaN bit) when a NaN value is loaded into a processor register. So at least one of the bits, the quiet NaN bit, you cannot use for storing arbitrary data.

Using the other bits, so long as the quiet NaN bit is set, is probably safe. But still there seems to be room for implementation-dependence here, and hence no guarantee.

This sort of problem is why normal language operations avoid doing anything that depends on the internal value of a NaN, and prefer to treat all NaNs as "just NaN".

According to the IEEE-754 standard, for non-signaling NaNs trailing bits should be preserved, and if they represent something canonical, they shall be preserved.

For an operation with quiet NaN inputs, other than maximum and minimum operations, if a floating-point result is to be delivered the result shall be a quiet NaN which should be one of the input NaNs. If the trailing significand field of a decimal input NaN is canonical then the bit pattern of that field shall be preserved if that NaN is chosen as the result NaN.

I wouldn't count on it. And for the leading sign bit, you definitely can't count on it.

For all other operations, this standard does not specify the sign bit of a NaN result, even when there is only one input NaN, or when the NaN is produced from an invalid operation.

The original IEEE-754 standard deliberately left the bits of a NaN up to the implementation. It did provide hints, such as

You might put the original memory address of where the NaN was created.

Meanwhile, arithmetic has specific rules about what to do with a NaN, and that has nothing to do with the bits in the bottom. I don't think it even says what to do when adding two NaNs -- keep the bits from one of them versus make up another set of bits. Just that the result must still be a NaN.

本文标签: javascriptAre JS engines allowed to change the bits of a NaNStack Overflow