EVALUATION
This problem is reproducible on winNT and linux. It is not reproducible on
solaris-sparc. I have verified that the problem has existed since DoubleBuffers
were introduced in jdk1.4.
In the case where the native byte order does not match the buffer's byte order,
this is the implementation of DirectDoubleBuffer.put:
public DoubleBuffer put(double x) {
unsafe.putDouble(ix(nextPutIndex()), Bits.swap(x));
return this;
}
Here is the implementation of DirectDoubleBuffer.put for matching byte orders:
public DoubleBuffer put(double x) {
unsafe.putDouble(ix(nextPutIndex()), (x));
return this;
}
The only difference between these two methods is the use of Bits.swap. We
never use Bits.swap for heap buffers.
-- iag@sfbay 2003-01-16
The problem here is that the DirectDoubleBuffer code is treating a swapped
double as a double. This often works, but not when the swapped bits happen to
represent a NaN. For the particular case described above we have
x = 0.5121609353879392,
y = Double.doubleToRawLongBits(x) = 0x3fe0639f5478f57f,
z = swap(y) = 0x7ff578549f63e03f, which represents a NaN.
At this point if we transform z back into a double and look at the resulting
raw bits we get
w = Double.doubleToRawLongBits(Double.longBitsToDouble(z))
= 0x7ffd78549f63e03f,
which is a different NaN but is the value that's actually stored in the buffer.
The long-to-double conversion, which is done in hardware, has set bit
0x0008000000000000 in the result. When this value is read from the buffer,
re-swapped, and then converted back to a double we get the incorrect value
Double.longBitsToDouble(swap(Double.doubleToRawLongBits(w)))
= 0.5121609353881665 != x.
The fix is to change the Direct{Float,Double}Buffer code to store and load
swapped doubles or floats as longs or ints, respectively. This is also a slight
performance improvement since there's no reason to convert swapped bits twice.
-- ###@###.### 2003/1/17
Some more detail on the previous evaluation. IEEE 754 arithmetic defines two
kinds of NaN, signaling and quiet. When signaling NaNs are used in an
operation, possibly including a load or store, they are changed into quiet
NaNs. The x86 and most other processors do this conversion by writing a 1 into
the highest bit of the significand, i.e. 0x0008000000000000L, which is the
change being observed. On the x86, this conversion happens when a float or
double signaling NaN is loading onto the x87 stack. As discussed in bug
4660849, x86 calling conventions require functions which return doubles to
return them on the top of the x87 stack. Therefore, as long as
longBitsToDouble is implemented as a normal function, the method cannot return
the bit pattern of a signaling NaN. Implementing the bitwise conversion
functions as intrinsics with load and store instructions may be able to avoid
this problem. SPARC does not perform a NaN conversion on floating-point load
or store.
Bit patterns this problem affects are
0xyyyyyyyy(7|f)ff([0-7])yyyy
where y is any bit pattern.
I agree with the fix proposed above.
###@###.### 2003-01-17
For the record, a quick-and-dirty test of 10^8 pseudorandom samples shows that
this error affects about 1 in 1000 random float values and about 1 in 8200
random double values.
-- ###@###.### 2003/1/20
The float bit patterns with this problem are
0xyyyy(7|f)f(8-B)y
where y is any bit pattern.
###@###.### 2003-01-29
|