benf.org :  other :  cfr :  double comparison

An interesting thing with NaNs

Thanks to Abe Crannaford for reporting this

As usual - this is (in retrospect, of course!) totally obvious, well documented, and shouldn't come as a surprise. The main reason I'm writing this up is because I was so surprised I'd got it wrong that I checked around the rest of the decompiler ecosystem, and it looks like (correct me if I'm wrong) every other java decompiler did too! (as of 2018/12), so it's worth mentioning!

Prior to 0.139, CFR incorrectly decompiled

	public static void t1(double d) {
		System.out.println(!(d < Double.NaN));
	}

	public static void t2(double d) {
		System.out.println(d >= Double.NaN);
	}
as
	public static void t1(double d) {
		System.out.println(d >= Double.NaN);
	}

	public static void t2(double d) {
		System.out.println(d >= Double.NaN);
	}

What about NaN

The above is wrong, because of NaN:

d < NaNfalse
d >= NaNfalse
!(d >= NaN)true

So what's the bytecode like?

  public static void t1(double);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: dload_0
       4: ldc2_w        #4                  // double NaNd
       7: dcmpg
       8: iflt          15
      11: iconst_1
      12: goto          16
      15: iconst_0
      16: invokevirtual #6                  // Method java/io/PrintStream.println:(Z)V
      19: return

  public static void t2(double);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: dload_0
       4: ldc2_w        #4                  // double NaNd
       7: dcmpl
       8: iflt          15
      11: iconst_1
      12: goto          16
      15: iconst_0
      16: invokevirtual #6                  // Method java/io/PrintStream.println:(Z)V
      19: return

Absolutely identical, apart from the crafty difference between dcmp*g* and dcmp*l*.

Fun!

	java -jar cfr-0.139.jar cfr_tests\org\benf\cfr\tests\FloatEvalTest2.class
	/*
	 * Decompiled with CFR 0.139.
	 */
	package org.benf.cfr.tests;

	import java.io.PrintStream;

	public class FloatEvalTest2 {
		public static void t1(double d) {
			System.out.println(!(d < Double.NaN));
		}

		public static void t2(double d) {
			System.out.println(d >= Double.NaN);
		}

		public static /* varargs */ void main(String ... arrstring) {
			FloatEvalTest2.t1(3.9);
			FloatEvalTest2.t2(3.9);
		}
	}

Last updated 01/2019