RFR 8158039 VarHandle float/double field/array access shouldsupport CAS/set/add atomics

timo.kinnunen at gmail.com timo.kinnunen at gmail.com
Thu Jun 2 18:19:50 UTC 2016


Regarding x86, I can induce an infinite loop in getAndAddFloat  on my computer if I simulate getFloatVolatile with the following implementation:

	static float getFloatVolatile(Object o, long offset) {
		return -0.0f + Float.intBitsToFloat(((int[]) o)[(int) offset]);

Note the added sum with -0.0f. This simulates the case where storing or subsequently reading a local variable changes the NaN value. With this implementation the first (largest) NaN to hang is 0xffbfffff. 

Some other curiosities: 

Adding -0.0 leaves all non-NaN floats with identical raw bits, including the negative zero. This isn’t the case if positive zero is used. 

When positive zero was used every non-NaN except negative zero remained same.

This method changes 8,388,606 of the NaNs and leaves 8,388,608 the same.

Have a nice day, 

Sent from Mail for Windows 10

From: Paul Sandoz
Sent: Thursday, June 2, 2016 17:26
To: Vladimir Ivanov
Cc: jdk9-dev; hotspot-dev developers
Subject: Re: RFR 8158039 VarHandle float/double field/array access shouldsupport CAS/set/add atomics

> On 2 Jun 2016, at 14:55, Vladimir Ivanov <vladimir.x.ivanov at oracle.com> wrote:
> Paul,
> Leaving practicality aspect of float/double API aside...
>> I am concerned that the CAS loops could in some cases loop without termination:
> Can you elaborate? I don't see how it is possible with the proposed patch.

The key aspect is in the JavaDoc snippet  in my previous email.

> On Float.intBitsToFloat:
> * <p>Note that this method may not be able to return a
> * {@code float} NaN with exactly same bit pattern as the
> * {@code int} argument.  IEEE 754 distinguishes between two
> * kinds of NaNs, quiet NaNs and <i>signaling NaNs</i>.  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 {@code intBitsToFloat} may
> * not be able to return a {@code float} with a signaling NaN
> * bit pattern.  Consequently, for some {@code int} values,
> * {@code floatToRawIntBits(intBitsToFloat(start))} may
> * <i>not</i> equal {@code 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.

I dunno if it can happen on x86 [*], but from reading the above I presume it could theoretically happen on some other hardware (what exactly i do not know).

Note that the loaded float value is passed to the weakCompareAndSwapFloatVolatile method as an argument. IIUC that might trigger the conversion from a signalling NaN to a quiet NaN, subtly changing the bits of the expected value that was loaded.


[*] Here is a simple program to induce a different on x86 (at least on my machine). To induce that i am adding 0.0f to the float value.

public class AllTheNaNs {

    public static void main(String[] args) {
        for (long bits = 0x7f800001; bits <= 0x7fffffff; bits++) {
            testBits((int) bits);
        for (long bits = 0xff800001; bits <= 0xffffffff; bits++) {
            testBits((int) bits);

    static void testBits(int vbits) {
        float v = Float.intBitsToFloat(vbits);
        assert Float.isNaN(v);

        float vv = id(v);
        assert Float.isNaN(vv);

        int vvbits = Float.floatToRawIntBits(vv);
        if (vvbits != vbits) {
            System.out.println(Integer.toHexString(vbits) + " " +
                               Integer.toHexString(vvbits) + " " +
                               Float.valueOf(v).equals(Float.valueOf(vv)) + " " +
                               Float.compare(v, vv));

    static float id(float f) {
        return f + 0.0f;

> Unless getFloatVolatile() or weakCompareAndSwapFloatVolatile() change the original value, it should not happen. And both functions preserve float value bits intact:
> +    @ForceInline
> +    public final boolean weakCompareAndSwapFloatVolatile(Object o, long offset,
> +                                                       float expected,
> +                                                       float x) {
> +        return weakCompareAndSwapIntVolatile(o, offset,
> + Float.floatToRawIntBits(expected),
> +                                             Float.floatToRawIntBits(x));
> +    }
> Float.floatToRawIntBits() makes it safe, but with Float.intBitsToFloat() used infinite looping can happen for NaNs.
> Best regards,
> Vladimir Ivanov

More information about the jdk9-dev mailing list