2015-09-07 24 views
16

documentation of std::hypot mówi, że:Kiedy używać std :: hypot `(x, y)` `nad std :: sqrt (x * x + y * y)`

Oblicza pierwiastek kwadratowy z suma kwadratów xiy, bez zbędnego przepełnienia lub niedomiaru na pośrednich etapach obliczeń.

walczę wyobrazić przypadek testowy gdzie std::hypot powinny być wykorzystywane w trywialny sqrt(x*x + y*y).

Poniższy test pokazuje, że std::hypot jest mniej więcej 20 razy wolniejszy od obliczeń naiwnych.

#include <iostream> 
#include <chrono> 
#include <random> 
#include <algorithm> 

int main(int, char**) { 
    std::mt19937_64 mt; 
    const auto samples = 10000000; 
    std::vector<double> values(2 * samples); 
    std::uniform_real_distribution<double> urd(-100.0, 100.0); 
    std::generate_n(values.begin(), 2 * samples, [&]() {return urd(mt); }); 
    std::cout.precision(15); 

    { 
     double sum = 0; 
     auto s = std::chrono::steady_clock::now(); 
     for (auto i = 0; i < 2 * samples; i += 2) { 
      sum += std::hypot(values[i], values[i + 1]); 
     } 
     auto e = std::chrono::steady_clock::now(); 
     std::cout << std::fixed <<std::chrono::duration_cast<std::chrono::microseconds>(e - s).count() << "us --- s:" << sum << std::endl; 
    } 
    { 
     double sum = 0; 
     auto s = std::chrono::steady_clock::now(); 
     for (auto i = 0; i < 2 * samples; i += 2) { 
      sum += std::sqrt(values[i]* values[i] + values[i + 1]* values[i + 1]); 
     } 
     auto e = std::chrono::steady_clock::now(); 
     std::cout << std::fixed << std::chrono::duration_cast<std::chrono::microseconds>(e - s).count() << "us --- s:" << sum << std::endl; 
    } 
} 

Więc pytam dla orientacji, gdy muszę używać std::hypot(x,y) celu uzyskania poprawnych wyników nad znacznie szybciej std::sqrt(x*x + y*y).

Wyjaśnienie: szukam odpowiedzi, które obowiązują podczas x i y są liczb zmiennoprzecinkowych. To znaczy. porównaj:

double h = std::hypot(static_cast<double>(x),static_cast<double>(y)); 

do:

double xx = static_cast<double>(x); 
double yy = static_cast<double>(y); 
double h = std::sqrt(xx*xx + yy*yy); 
+1

Myślę, że powinieneś także porównać to z 'std :: abs (std :: complex (x, y))' jak w [std :: hypot] (http://en.cppreference.com/w/ cpp/numeric/math/hypot) strona –

+0

Późno, ale dokumentacja cppreference mówi również jako notatka (więc nie ma gwarancji na podstawie normy), że "Wdrożenia zwykle gwarantują precyzję mniejszą niż 1 ulp (jednostki na ostatnim miejscu)." 'x * x + y * y' może stracić kilka bitów dokładności, jeśli z zaokrągleniem do najbliższego zestawu. Oznacza to, że 'std :: sqrt (x * x + y * y)' może być wyłączony przez nieco lub dwa. Aby uzyskać tę gwarancję, potrzebny jest lepszy algorytm niż 'std :: sqrt (x * x + y * y)'. (kontynuacja) –

+0

Co gorsza, załóżmy, że zmarnowałeś zaokrąglenie? To z pewnością stanie na drodze do osiągnięcia tej precyzji sub-ulp. 'hypot' musi ustawić zaokrąglenie, aby osiągnąć tę dokładność, a następnie przywrócić zaokrąglenie do swoich ustawień. To ustawienie i resetowanie zaokrąglania powoduje, że 'std: hypot (x, y)' jest znacznie wolniejsze niż 'std :: sqrt (x * x + y * y)'. –

Odpowiedz

19

Odpowiedź jest w dokumentacji, którą cytowany

Oblicza pierwiastek kwadratowy z sumy kwadratów xiy, bez zbędnej przepełnienie lub niedomiar na pośrednich etapach obliczeń.

If x*x + y*y przepełnienia, a następnie, jeśli przeprowadzają obliczenia ręcznie, dostaniesz złą odpowiedź. Jeśli jednak używasz std::hypot, gwarantuje to, że obliczenia pośrednie nie będą przepełnione.

Możesz zobaczyć przykład tej rozbieżności here.

Jeśli pracujesz z liczbami, o których wiesz, że nie przepełnią one odpowiedniej reprezentacji twojej platformy, możesz z powodzeniem użyć naiwnej wersji.

+2

Parametry 'std :: hypot' są zmiennymi typu pływającego-pont, więc jeśli użyjesz tutaj int to również będzie promowane do zmiennoprzecinkowej. –

+0

Mogłem nie być w 100% czysty w moim pytaniu. Interesuje mnie tylko różnica między tymi dwoma, gdy używane są argumenty precyzji "double". Ponieważ przepełnienie liczby całkowitej jest obsługiwane przez promocję typu, a nie przez funkcję 'hypot'. Ale coś w "hypot" sprawia, że ​​jest 20 razy wolniejsze niż naiwna implementacja (nawet przy promocji typu). Dlaczego to i kiedy uratuje moją tushie? –

+0

Jak widać [w tym przykładzie] (http://coliru.stacked-crooked.com/a/d957adbf7ab753a0), nie ma rozbieżności przy użyciu argumentów podwójnej precyzji. W szczególności szukam "podwójnego x, y", w którym pojawi się różnica. –