В настоящее время я работаю над математической библиотекой Java, которая будет включать в себя множество правильно округленных функций (то есть sqrt, cbrt, exp, sin, gamma и ln). Я уже использовал вавилонский метод, чтобы написать алгоритм с квадратным корнем, который является правильным с точностью до 1 ulp правильного ответа. Однако я не могу понять, как правильно рассчитать, каким образом число должно быть округлено, чтобы представить наилучшее возможное приближение к фактическому квадратному корню из входных данных. Ответы, содержащие принципы, которые могут быть распространены на другие функции, были бы предпочтительнее, но я слышал, что sqrt - более простой случай, чем многие трансцендентные функции, и специальные решения также будут высоко оценены.
Кроме того, вот исправленная версия моего кода на момент первоначального представления этого вопроса:
public static double sqrt(double x) {
long bits = Double.doubleToLongBits(x);
// NaN and non-zero negatives:
if (Double.isNaN(x) || x < 0) return Double.NaN;
// +-0 and 1:
if (x == 0d || x == 1d) return x;
// Halving the exponent to come up with a good initial guess:
long exp = bits << 1;
exp = (exp - 0x7fe0000000000000L >> 1) + 0x7fe0000000000000L >>> 1 & 0x7ff0000000000000L;
double guess = Double.longBitsToDouble(bits & 0x800fffffffffffffL | exp);
double nextUp, nextDown, guessSq, nextUpSq, nextDownSq;
// Main loop:
while (true) {
guessSq = guess * guess;
if (guessSq == x) return guess;
nextUp = Math.nextUp(guess);
nextUpSq = nextUp * nextUp;
if (nextUpSq == x) return nextUp;
if (guessSq < x && x < nextUpSq) {
double z = x / nextUp;
if (z * nextUp > x) z = Math.nextDown(z);
return z < nextUp ? nextUp : guess;
}
nextDown = Math.nextDown(guess);
nextDownSq = nextDown * nextDown;
if (nextDownSq == x) return nextDown;
if (nextDownSq < x && x < guessSq) {
double z = x / guess;
if (z * guess > x) z = Math.nextDown(z);
return z < guess ? guess : nextDown;
}
// Babylonian method:
guess = 0.5 * (guess + x / guess);
}
}
Как видите, я использовал деление в качестве теста. Однако я считаю, что для округления требуется округление до 0, что, очевидно, не происходит в Java.