title: IEEE-754 double (64-bit floating point) vs. long (64-bit integer) revisited
body: <p>I'm revisiting a question (<a href="https://stackoverflow.com/questions/1601646">How to test if numeric conversion will change value?</a>) that as far as I was concerned was fully solved. The problem was to detect when a particular numeric value would overflow JavaScript's IEEE-754 Number type. The previous question was using C# and the marked answer worked perfectly.</p>
  <p>Now I'm doing the exact same task but this time in Java and it doesn't work. AFAIK, Java uses IEEE-754 for its <code>double</code> data type. So I should be able to cast it back and forth to force the loss of precision but it round trips. Baffled by this I started poking deeper in Java and now I'm really confused.</p>
  <p>In both C# and Java, the min and max values for long are the same:</p>
  <pre><code>long MIN_VALUE = -9223372036854775808L;
  long MAX_VALUE = 9223372036854775807L;
  </code></pre>
  <p>AFAIK, these values are outside the representable numbers in IEEE-754 because of the fixed bits reserved for the exponent and sign.</p>
  <pre><code>// this fails in browsers that have stuck with the pure ECMAScript Number format
  var str = Number(-9223372036854775808).toFixed();
  if (&quot;-9223372036854775808&quot; !== str) { throw new Error(&quot;Overflow!&quot;); }
  </code></pre>
  <p>This returns <code>false</code> for (value = -9223372036854775808L) in Java:</p>
  <pre><code>boolean invalidIEEE754(long value) {
      try {
          return ((long)((double)value)) != value;
      } catch (Exception ex) {
          return true;
      }
  }
  </code></pre>
  <p>This returns <code>false</code> for (value = -9223372036854775808L) in Java:</p>
  <pre><code>boolean invalidIEEE754(long value) {
      // trying to get closer to the actual representation and
      // being more explicit about conversions
      long bits = Double.doubleToLongBits(Long.valueOf(value).doubleValue());
      long roundtrip = Double.valueOf(Double.longBitsToDouble(bits)).longValue();
      return (value != roundtrip);
  }
  </code></pre>
  <p>This returns <code>true</code> for (value = -9223372036854775808L) but is less accurate:</p>
  <pre><code>boolean invalidIEEE754(long value) {
      return (0x0L != (0xFFF0000000000000L &amp; (value &lt; 0L ? -value : value)));
  }
  </code></pre>
  <p>Why does this work this way? Am I missing something like compiler optimization, e.g. is the compiler detecting my conversions and &quot;fixing&quot; them for me?</p>
  <p><strong>Edit:</strong> Adding test case by request. All three of these tests fail:</p>
  <pre><code>import static org.junit.Assert.*;
  import org.junit.Test;
  
  public class FooTests {
  
      @Test
      public void ieee754One() {
          assertTrue(((long)((double)Long.MIN_VALUE)) != Long.MIN_VALUE);
      }
  
      @Test
      public void ieee754Two() {
          long bits = Double.doubleToLongBits(Long.valueOf(Long.MIN_VALUE).doubleValue());
          long roundtrip = Double.valueOf(Double.longBitsToDouble(bits)).longValue();
  
          assertTrue(Long.MIN_VALUE != roundtrip);
      }
  
      @Test
      public void ieee754Three() {
          long bits = Double.doubleToRawLongBits(Long.valueOf(Long.MIN_VALUE).doubleValue());
          long roundtrip = Double.valueOf(Double.longBitsToDouble(bits)).longValue();
  
          assertTrue(Long.MIN_VALUE != roundtrip);
      }
  }
  </code></pre>