Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

В общем, нет. Однако вы можете использовать strictfp выражения:

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

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

Ответ 2

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

Ответ 3

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

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

Итак, если у вас есть код вроде:

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

и это не в контексте strictfp, возможно, что во время выполнения у вас будут различия между разными JVM. Это связано с тем, что при оценке выражения d1 * d2/d3 промежуточный результат d1 * d2 используется в выражении "промежуточный результат" /d 3, а JVM может использовать форматы с расширением float-extended-exponent и double-extended-exponent для хранения "промежуточного результата".

Чтобы обойти это, вы можете использовать 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);
    }
}