Будут ли операции с плавающей запятой на JVM давать одинаковые результаты на всех платформах?


Я использую Java в приложении, работающем на нескольких машинах, и все машины должны получать одинаковые результаты для математических операций. Безопасно ли использовать примитивы Java с плавающей запятой? Или я должен просто использовать математическую библиотеку с фиксированной точкой?

3 56

3 ответа:

Не вообще, нет. Тем не менее, вы можете использовать strictfp выражения:

в выражении FP-strict все промежуточные значения должны быть элементами набора значений float или набора значений double, что означает, что результаты всех выражений FP-strict должны быть предсказаны арифметикой IEEE 754 на операндах, представленных с использованием одиночных и двойных форматов.

в выражении, которое не является FP-строгим, некоторая свобода действий предоставляется для реализация для использования расширенного диапазона экспонент для представления промежуточных результатов; чистый эффект, грубо говоря, заключается в том, что расчет может дать "правильный ответ" в ситуациях, когда исключительное использование набора значений с плавающей запятой или двойного набора значений может привести к переполнению или недоливу.

кроме strictfp, там же StrictMath что требует предсказуемости результатов для трансцендентальных и других функций.

JVM должен последовательно реализовывать спецификацию IEEE, и эта спецификация является очень технической и точной. Примитивы с плавающей точкой float и double are то же самое на всех платформах.

разница заключается только в обработке промежуточных результатов и в том, что реализация виртуальной машины может использовать форматы float-extended-exponent и double-extended-exponent при оценке выражений с участием локальных объектов в одном кадре исполнение.

Так что если у вас есть код типа:

double d1 = 0.342;
double d2 = 1.328479;
double d3 = 4.99384728796;
System.out.println(d1 * d2 / d3);

и это не в строгом контексте FP, возможно, что у вас будут различия между различными JVMs во время выполнения. Это связано с тем, что при оценке выражения d1*d2/d3 промежуточный результат d1*d2 используется в выражении "промежуточный результат"/d3, а JVM может использовать форматы с плавающей расширенной экспонентой и двойной расширенной экспонентой для хранения "промежуточного результата".

получить вокруг этого вы можете использовать strictfp или StrictMath, как ответили здесь Другие, или избегать использования промежуточных результатов в своих выражениях. Один из способов сделать это-сохранить любой промежуточный результат в куче. Например, следующим образом:

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);
    }
}