2010-09-21 33 views
27

Tôi đã thực hiện một số thử nghiệm với C++hypot()JavaMath.hypot. Cả hai dường như chậm hơn đáng kể so với sqrt(a*a + b*b). Đó có phải là do độ chính xác cao hơn không? Phương pháp nào để tính toán sử dụng chức năng lạm dụng hypot? Đáng ngạc nhiên là tôi không thể tìm thấy bất kỳ dấu hiệu nào về hiệu suất kém trong tài liệu.Tại sao hàm hypot() quá chậm?

+5

'chậm hơn đáng kể' là gì? Bạn có thể định lượng giá trị này không? Bạn đã sử dụng một hồ sơ? Bạn đã chạy thử bao nhiêu lần? Bạn có thể mô tả thử nghiệm của mình (DOE) không? – linuxuser27

+3

Trong Java nó chậm hơn một hệ số ~ 7, trong C++ ~ 10. Chúng tôi thấy rằng độc lập với đồng nghiệp của tôi khi thử nghiệm một trong những vấn đề lập trình cho cuộc thi lập trình sắp tới tại một trường đại học. – Leonid

+1

@ linuxuser27: và hai người upvoted bình luận của mình, kiểm tra Ravi Gummadi +9 upvoted câu trả lời cho giác ngộ. – SyntaxT3rr0r

Trả lời

25

Đây không phải là chức năng sqrt đơn giản. Bạn nên kiểm tra liên kết này để thực hiện các thuật toán: http://www.koders.com/c/fid7D3C8841ADC384A5F8DE0D081C88331E3909BF3A.aspx

Nó có khi vòng lặp để kiểm tra tụ

/* 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 (Update từ JM):

Here là bản gốc Moler còn-Morrison giấy, và here là một sự theo dõi tốt đẹp do Dubrulle.

+0

Sẽ rất hay để cung cấp cho ví dụ trong trường hợp hàm này tốt hơn phương thức 'sqrt'. – schnaader

+2

@schnaader - đọc trang được liên kết, lý do là đúng ở đầu (phiên bản ngắn, phương pháp ngây thơ có thể tràn khi không nên) – Nir

+2

Điều này quan trọng đối với các giá trị rất lớn của x và y. Ví dụ: nếu x và y là x^2 + y^2 lớn hơn MAX_DOUBLE, phiên bản sqrt (x^2 + y^2) sẽ không thành công. Tuy nhiên, vì phương pháp này không bao giờ tạo ra một giá trị lớn hơn nhiều so với x hoặc y, nên nó phải an toàn trong những tình huống đó. – pfhayes

3

Đây là một thực hiện nhanh hơn, mà kết quả là cũng gần gũi hơn với java.lang.Math.hypot: (NB: để thực hiện Delorie của, cần thêm xử lý NaN và + vô cực đầu vào)

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

Tôi đã tìm thấy Math.hypot() rất chậm. Tôi tìm thấy tôi có thể mã một phiên bản java nhanh chóng bằng cách sử dụng cùng một thuật toán tạo ra kết quả giống hệt nhau. Điều này có thể được sử dụng để thay thế nó.

/** 
* <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); 
    } 
} 
Các vấn đề liên quan