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);
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 –
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) –
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)'. –