Я провел некоторые испытания с помощью C++
hypot()
и Java
Math.hypot
. Они кажутся значительно медленнее, чем sqrt(a*a + b*b)
. Это из-за лучшей точности? Какой метод расчета функции гипотенузы hypot
использует? Удивительно, но я не мог найти никаких признаков плохой работы в документации.Почему функция hypot() работает так медленно?
ответ
Это не простая функция sqrt. Вы должны проверить эту ссылку для реализации алгоритма: http://www.koders.com/c/fid7D3C8841ADC384A5F8DE0D081C88331E3909BF3A.aspx
имеет время цикла для проверки сходимости
/* Slower but safer algorithm due to Moler and Morrison. Never
produces any intermediate result greater than roughly the
larger of X and Y. Should converge to machine-precision
accuracy in 3 iterations. */
double r = ratio*ratio, t, s, p = abig, q = asmall;
do {
t = 4. + r;
if (t == 4.)
break;
s = r/t;
p += 2. * s * p;
q *= s;
r = (q/p) * (q/p);
} while (1);
EDIT (Обновление от JM):
Here является оригинальным Moler-Morrison бумага и here - прекрасное продолжение благодаря Dubrulle.
Было бы неплохо дать примеры, где эта функция лучше, чем метод 'sqrt'. – schnaader
@schnaader - прочитайте связанную страницу, причина в верхней части (короткая версия, наивный метод может переполняться, когда это не должно) – Nir
Это имеет значение для очень больших значений x и y. Например, если x и y таковы, что x^2 + y^2 больше MAX_DOUBLE, версия sqrt (x^2 + y^2) потерпит неудачу. Однако, поскольку этот метод никогда не дает значение, которое намного больше, чем x или y, оно должно быть безопасным в этих ситуациях. – pfhayes
Вот быстрее реализация, результаты которого также ближе к java.lang.Math.hypot: (NB: для реализации Delorie, в нужно добавить обработку Нан и + -Infinity входы)
private static final double TWO_POW_450 = Double.longBitsToDouble(0x5C10000000000000L);
private static final double TWO_POW_N450 = Double.longBitsToDouble(0x23D0000000000000L);
private static final double TWO_POW_750 = Double.longBitsToDouble(0x6ED0000000000000L);
private static final double TWO_POW_N750 = Double.longBitsToDouble(0x1110000000000000L);
public static double hypot(double x, double y) {
x = Math.abs(x);
y = Math.abs(y);
if (y < x) {
double a = x;
x = y;
y = a;
} else if (!(y >= x)) { // Testing if we have some NaN.
if ((x == Double.POSITIVE_INFINITY) || (y == Double.POSITIVE_INFINITY)) {
return Double.POSITIVE_INFINITY;
} else {
return Double.NaN;
}
}
if (y-x == y) { // x too small to substract from y
return y;
} else {
double factor;
if (x > TWO_POW_450) { // 2^450 < x < y
x *= TWO_POW_N750;
y *= TWO_POW_N750;
factor = TWO_POW_750;
} else if (y < TWO_POW_N450) { // x < y < 2^-450
x *= TWO_POW_750;
y *= TWO_POW_750;
factor = TWO_POW_N750;
} else {
factor = 1.0;
}
return factor * Math.sqrt(x*x+y*y);
}
}
http://steve.hollasch.net/cgindex/math/pythag-root.txt
предполагает, что чем быстрее реализация с использованием SQRT() является квадратичной по сравнению с кубической для Moler & Morrison, примерно с теми же характеристиками переполнения.
Я нашел Math.hypot(), чтобы быть ужасно медленным. Я обнаружил, что могу скопировать быструю версию Java, используя тот же алгоритм, который дает идентичные результаты. Это можно использовать для его замены.
/**
* <b>hypot</b>
* @param x
* @param y
* @return sqrt(x*x +y*y) without intermediate overflow or underflow.
* @Note {@link Math#hypot} is unnecessarily slow. This returns the identical result to
* Math.hypot with reasonable run times (~40 nsec vs. 800 nsec).
* <p>The logic for computing z is copied from "Freely Distributable Math Library"
* fdlibm's e_hypot.c. This minimizes rounding error to provide 1 ulb accuracy.
*/
public static double hypot(double x, double y) {
if (Double.isInfinite(x) || Double.isInfinite(y)) return Double.POSITIVE_INFINITY;
if (Double.isNaN(x) || Double.isNaN(y)) return Double.NaN;
x = Math.abs(x);
y = Math.abs(y);
if (x < y) {
double d = x;
x = y;
y = d;
}
int xi = Math.getExponent(x);
int yi = Math.getExponent(y);
if (xi > yi + 27) return x;
int bias = 0;
if (xi > 510 || xi < -511) {
bias = xi;
x = Math.scalb(x, -bias);
y = Math.scalb(y, -bias);
}
// translated from "Freely Distributable Math Library" e_hypot.c to minimize rounding errors
double z = 0;
if (x > 2*y) {
double x1 = Double.longBitsToDouble(Double.doubleToLongBits(x) & 0xffffffff00000000L);
double x2 = x - x1;
z = Math.sqrt(x1*x1 + (y*y + x2*(x+x1)));
} else {
double t = 2 * x;
double t1 = Double.longBitsToDouble(Double.doubleToLongBits(t) & 0xffffffff00000000L);
double t2 = t - t1;
double y1 = Double.longBitsToDouble(Double.doubleToLongBits(y) & 0xffffffff00000000L);
double y2 = y - y1;
double x_y = x - y;
z = Math.sqrt(t1*y1 + (x_y*x_y + (t1*y2 + t2*y))); // Note: 2*x*y <= x*x + y*y
}
if (bias == 0) {
return z;
} else {
return Math.scalb(z, bias);
}
}
- 1. Почему эта функция работает так медленно?
- 2. Почему Дискурс работает так медленно?
- 3. Почему генитоция работает так медленно?
- 4. Почему GestureOverlayView работает так медленно?
- 5. Почему аана работает так медленно?
- 6. Почему функция соединения MATLAB так медленно?
- 7. Почему так медленно вызывается функция в Javascript?
- 8. Почему эта функция javascript так медленно работает в Firefox?
- 9. numpy.vectorize: Почему так медленно?
- 10. Почему String.toUpperCase() так медленно?
- 11. Почему статистика.mean() так медленно?
- 12. Почему ShowGridLines так медленно?
- 13. Почему TestComplete так медленно?
- 14. Почему read.csv так медленно?
- 15. Почему PTVS так медленно?
- 16. Почему Array.splice() так медленно?
- 17. Почему видеоизображение так медленно?
- 18. Почему array.min так медленно?
- 19. Почему glUseProgram так медленно?
- 20. Почему валара так медленно?
- 21. Почему Collections.counter так медленно?
- 22. Почему так медленно?
- 23. MySql: Почему этот запрос работает так медленно?
- 24. Почему мой проект java работает так медленно?
- 25. Почему мой smo работает так медленно?
- 26. Почему код работает на Azure так медленно?
- 27. Почему мой ViewPager работает так медленно?
- 28. Почему Javascript работает так медленно на iPhone?
- 29. Почему эмулятор Android работает так позорно медленно?
- 30. Почему Minitest работает так медленно для меня?
Что такое «значительно медленнее»? Можете ли вы количественно оценить это значение? Вы использовали профилировщик? Сколько раз вы проводили тесты? Можете ли вы описать свой эксперимент (DOE)? – linuxuser27
В Java он был медленнее в ~ 7, в C++ ~ 10. Мы обнаружили, что независимо от моего коллеги при тестировании одной из проблем программирования для предстоящего конкурса программирования в университете. – Leonid
@ linuxuser27: и два человека, которые поддержали его комментарий, проверьте Рави Гуммади +9 ответ на вопрос о просветлении. – SyntaxT3rr0r