I'm revisiting a question (How to test if numeric conversion will change value?) 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.
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 double 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.
In both C# and Java, the min and max values for long are the same:
long MIN_VALUE = -9223372036854775808L;
long MAX_VALUE = 9223372036854775807L;
AFAIK, these values are outside the representable numbers in IEEE-754 because of the fixed bits reserved for the exponent and sign.
// this fails in browsers that have stuck with the pure ECMAScript Number format
var str = Number(-9223372036854775808).toFixed();
if ("-9223372036854775808" !== str) { throw new Error("Overflow!"); }
This returns false
for (value = -9223372036854775808L) in Java:
boolean invalidIEEE754(long value) {
try {
return ((long)((double)value)) != value;
} catch (Exception ex) {
return true;
}
}
This returns false
for (value = -9223372036854775808L) in Java:
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);
}
This returns true
for (value = -9223372036854775808L) but is less accurate:
boolean invalidIEEE754(long value) {
return (0x0L != (0xFFF0000000000000L & (value < 0L ? -value : value)));
}
Why does this work this way? Am I missing something like compiler optimization, e.g. is the compiler detecting my conversions and "fixing" them for me?
Edit: Adding test case by request. All three of these tests fail:
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);
}
}