Will floating point operations on the JVM give the same results on all platforms?
I'm using Java in an application running on multiple machines, and all machines need to get the same results for mathematical operations. Is it safe to use Java's floating point primitives? Or should I just use a fixed-point math library?
Not in general, no. However, you can use strictfp
expressions:
Within an FP-strict expression, all intermediate values must be elements of the float value set or the double value set, implying that the results of all FP-strict expressions must be those predicted by IEEE 754 arithmetic on operands represented using single and double formats.
Within an expression that is not FP-strict, some leeway is granted for an implementation to use an extended exponent range to represent intermediate results; the net effect, roughly speaking, is that a calculation might produce "the correct answer" in situations where exclusive use of the float value set or double value set might result in overflow or underflow.
In addition to strictfp
, there's also StrictMath
which requires that the results be predictable for transcendental and other functions.
The JVM should implement the IEEE specification consistently and this specification is very technical and precise. The floating point primitives of float and double are the same on all platforms.
The difference is only in the treatment of intermediate results, and that the virtual machine implementation may use float-extended-exponent and double-extended-exponent formats while evaluating expressions involving locals within the same frame of execution.
So if you have code like:
double d1 = 0.342;
double d2 = 1.328479;
double d3 = 4.99384728796;
System.out.println(d1 * d2 / d3);
and this is not in a strictfp context, it is possible that you will have differences across different JVMs at runtime. This is because in the evaluation of the expression d1*d2/d3 the intermediate result of d1*d2 is used in the expression "intermediate result"/d3 and the JVM might be using float-extended-exponent and double-extended-exponent formats to store the "intermediate result".
To get around this, you can use strictfp or StrictMath as others have answered here, OR avoid using intermediate results in your expressions. One way to do that is to store any intermediate result on the heap. For instance as follows:
class MyClass {
static double d1 = 0.342;
static double d2 = 1.328479;
static double d3 = 4.99384728796;
static double intermediate_result = 0d;
public static void main(String[] args) {
intermediate_result = d1 * d2;
System.out.println(intermediate_result / d3);
}
}