Po aktualizacji do wersji Java 1.8.0_20 nasz system testowy zgłosił błędy, ale kod nie został zmieniony. Dowiedziałem się, że Math.pow()
wywoływana z dokładnie tymi samymi parametrami wejściowymi daje różne wyniki przy zgłoszonych połączeniach. W języku Java 1.8.0_11 zachowuje się zgodnie z oczekiwaniami i zwraca zawsze tę samą wartość, ale w przypadku języka Java 1.8.0_20 i wyższych czasami zwraca nieco inne wartości.Math.pow daje różne wyniki po wielokrotnych połączeniach
Jest to podobne do pytania Math.pow yields different result depending on java version, ale inne, ponieważ wyniki pow() różnią się w obrębie jednej maszyny wirtualnej.
Poniższy test nie powiedzie się, gdy JUint prowadzony pod Java 1.8.0_20 i wyższy
import static org.junit.Assert.assertEquals;
import java.util.function.BiFunction;
import org.junit.BeforeClass;
import org.junit.Test;
public class PowerTest {
private static final int N = 1000000;
private static final double base = 5350.456329377186;
private static final double exp = 2.0;
private static double eval(final BiFunction<Double, Double, Double> f) {
return f.apply(base, exp);
}
private void loop(final BiFunction<Double, Double, Double> f) {
final double x = eval(f);
for (int i = 0; i < N; i++) {
final double p = eval(f);
assertEquals("i=" + i, x, p, 0);
}
}
@BeforeClass
public static void info() {
System.out.println("Java " + System.getProperty("java.version"));
}
@Test
public void mathPow() {
loop(Math::pow);
}
@Test
public void strictMathPow() {
loop(StrictMath::pow);
}
}
test nie powiedzie pod Java 1.8.0_11 lub jeśli hotspot jest włączony razem z -Xint
. Ścisła wersja matematyczna pow() daje spójne wyniki. Podejrzewam, że hotspot JIT wykonuje pewne optymalizacje, które przełączają się na inną implementację pow(), co daje różne wyniki dla pewnych wartości wejściowych. Funkcja matematyczna powinna być deterministyczna i powinna dawać spójne i powtarzalne wyniki.
Czy to błąd lub funkcja?